Skip to content

Commit

Permalink
add why so quiet
Browse files Browse the repository at this point in the history
  • Loading branch information
rwxrob committed Dec 19, 2024
1 parent 8dae2d5 commit 6b4c5c4
Show file tree
Hide file tree
Showing 6 changed files with 257 additions and 1 deletion.
2 changes: 2 additions & 0 deletions adoc/shut-the-faq-up/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,6 @@ include::why-perl-rocks.adoc[]

include::why-another-go-book.adoc[]

include::why-so-quiet.adoc[]

include::why-asciidoc-not-mkdocs.adoc[]
3 changes: 3 additions & 0 deletions adoc/shut-the-faq-up/why-so-quiet.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
= Why is your stream so quiet?

I learned the hard way that muting everything is best when just sharing a terminal in over-the-shoulder mode. For more on that and other lessons, see my perpetually unpublished link:https://rwxrob.github.io/books/bad-strimmer/[_Bad Strimmer_].
30 changes: 30 additions & 0 deletions adoc/terminal-velocity/golang/yt2wee/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
module github.com/rwxrob/books/adoc/terminal-velocity/golang/yt2wee

go 1.23.4

require google.golang.org/api v0.213.0

require (
cloud.google.com/go/auth v0.13.0 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.6 // indirect
cloud.google.com/go/compute/metadata v0.6.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/google/s2a-go v0.1.8 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
github.com/googleapis/gax-go/v2 v2.14.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect
go.opentelemetry.io/otel v1.29.0 // indirect
go.opentelemetry.io/otel/metric v1.29.0 // indirect
go.opentelemetry.io/otel/trace v1.29.0 // indirect
golang.org/x/crypto v0.31.0 // indirect
golang.org/x/net v0.32.0 // indirect
golang.org/x/oauth2 v0.24.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/text v0.21.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 // indirect
google.golang.org/grpc v1.67.1 // indirect
google.golang.org/protobuf v1.35.2 // indirect
)
61 changes: 61 additions & 0 deletions adoc/terminal-velocity/golang/yt2wee/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
cloud.google.com/go/auth v0.13.0 h1:8Fu8TZy167JkW8Tj3q7dIkr2v4cndv41ouecJx0PAHs=
cloud.google.com/go/auth v0.13.0/go.mod h1:COOjD9gwfKNKz+IIduatIhYJQIc0mG3H102r/EMxX6Q=
cloud.google.com/go/auth/oauth2adapt v0.2.6 h1:V6a6XDu2lTwPZWOawrAa9HUK+DB2zfJyTuciBG5hFkU=
cloud.google.com/go/auth/oauth2adapt v0.2.6/go.mod h1:AlmsELtlEBnaNTL7jCj8VQFLy6mbZv0s4Q7NGBeQ5E8=
cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM=
github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw=
github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA=
github.com/googleapis/gax-go/v2 v2.14.0 h1:f+jMrjBPl+DL9nI4IQzLUxMq7XrAqFYB7hBPqMNIe8o=
github.com/googleapis/gax-go/v2 v2.14.0/go.mod h1:lhBCnjdLrWRaPvLWhmc8IS24m9mr07qSYnHncrgo+zk=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8=
go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw=
go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8=
go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc=
go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8=
go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4=
go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs=
golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE=
golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
google.golang.org/api v0.213.0 h1:KmF6KaDyFqB417T68tMPbVmmwtIXs2VB60OJKIHB0xQ=
google.golang.org/api v0.213.0/go.mod h1:V0T5ZhNUUNpYAlL306gFZPFt5F5D/IeyLoktduYYnvQ=
google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 h1:M0KvPgPmDZHPlbRbaNU1APr28TvwvvdUPlSv7PUvy8g=
google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28/go.mod h1:dguCy7UOdZhTvLzDyt15+rOrawrpM4q7DD9dQ1P11P4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 h1:8ZmaLZE4XWrtU3MyClkYqqtl6Oegr3235h7jxsDyqCY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU=
google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E=
google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io=
google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
151 changes: 151 additions & 0 deletions adoc/terminal-velocity/golang/yt2wee/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
package main

import (
"context"
"fmt"
"log"
"sync"
"time"

"google.golang.org/api/option"
"google.golang.org/api/youtube/v3"
)

const (
apiKey = "YOUR_YOUTUBE_API_KEY" // Replace with your API key
channelID = "YOUR_CHANNEL_ID" // Replace with your channel ID
pollInterval = 5 * time.Second // Adjust as needed
expiryTime = 10 * time.Minute // Time after which entries expire
cleanupInterval = 1 * time.Minute // Interval to run cleanup
)

// ChatMessage represents a chat message from the source
type ChatMessage struct {
User string
Message string
}

// Global map with timestamps and a mutex for thread safety
var (
seenMessages = make(map[string]time.Time)
mu sync.Mutex
)

// fetchChatMessages fetches messages from YouTube Live Chat and deduplicates them
func fetchChatMessages(service *youtube.Service, liveChatID string, nextPageToken string) (messages []ChatMessage, newNextPageToken string, err error) {
call := service.LiveChatMessages.List(liveChatID, []string{"snippet", "authorDetails"}).Key(apiKey)
if nextPageToken != "" {
call = call.PageToken(nextPageToken)
}

response, err := call.Do()
if err != nil {
return nil, "", fmt.Errorf("error fetching live chat messages: %w", err)
}

mu.Lock()
defer mu.Unlock()

now := time.Now()
for _, item := range response.Items {
if _, exists := seenMessages[item.Id]; !exists {
seenMessages[item.Id] = now // Mark the message as seen with the current timestamp
messages = append(messages, ChatMessage{
User: item.AuthorDetails.DisplayName,
Message: item.Snippet.DisplayMessage,
})
}
}

return messages, response.NextPageToken, nil
}

// cleanupSeenMessages removes old entries from the seenMessages map
func cleanupSeenMessages() {
for {
time.Sleep(cleanupInterval)

mu.Lock()
now := time.Now()
for id, timestamp := range seenMessages {
if now.Sub(timestamp) > expiryTime {
delete(seenMessages, id)
}
}
mu.Unlock()
}
}

// getLiveChatID fetches the live chat ID for the current live stream
func getLiveChatID(service *youtube.Service) (string, error) {
call := service.Search.List([]string{"snippet"}).
ChannelId(channelID).
EventType("live").
Type("video").
Key(apiKey)

response, err := call.Do()
if err != nil {
return "", fmt.Errorf("error fetching live broadcasts: %w", err)
}

if len(response.Items) == 0 {
return "", fmt.Errorf("no live broadcasts found for channel ID %s", channelID)
}

videoID := response.Items[0].Id.VideoId

videoCall := service.Videos.List([]string{"liveStreamingDetails"}).
Id(videoID).
Key(apiKey)

videoResponse, err := videoCall.Do()
if err != nil {
return "", fmt.Errorf("error fetching video details: %w", err)
}

if len(videoResponse.Items) == 0 {
return "", fmt.Errorf("no video details found for video ID %s", videoID)
}

liveChatID := videoResponse.Items[0].LiveStreamingDetails.ActiveLiveChatId
if liveChatID == "" {
return "", fmt.Errorf("live chat ID not found for video ID %s", videoID)
}

return liveChatID, nil
}

func main() {
ctx := context.Background()
service, err := youtube.NewService(ctx, option.WithAPIKey(apiKey))
if err != nil {
log.Fatalf("Error creating YouTube service: %v", err)
}

liveChatID, err := getLiveChatID(service)
if err != nil {
log.Fatalf("Error getting live chat ID: %v", err)
}

// Start the cleanup routine
go cleanupSeenMessages()

var nextPageToken string
for {
messages, newNextPageToken, err := fetchChatMessages(service, liveChatID, nextPageToken)
if err != nil {
log.Printf("Error fetching chat messages: %v", err)
time.Sleep(pollInterval)
continue
}

for _, msg := range messages {
fmt.Printf("%s: %s\n", msg.User, msg.Message)
// Here, you would send the message to WeeChat FIFO
}

nextPageToken = newNextPageToken
time.Sleep(pollInterval)
}
}
11 changes: 10 additions & 1 deletion docs/shut-the-faq-up/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,7 @@ <h1>Shut the FAQ up</h1>
</ul>
</li>
<li><a href="#_why_another_go_book">Why another Go book?</a></li>
<li><a href="#_why_is_your_stream_so_quiet">Why is your stream so quiet?</a></li>
<li><a href="#_why_asciidoc_instead_of_mkdocs">Why AsciiDoc instead of MkDocs?</a></li>
</ul>
</div>
Expand Down Expand Up @@ -1657,6 +1658,14 @@ <h2 id="_why_another_go_book"><a class="link" href="#_why_another_go_book">Why a
</div>
</div>
<div class="sect1">
<h2 id="_why_is_your_stream_so_quiet"><a class="link" href="#_why_is_your_stream_so_quiet">Why is your stream so quiet?</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>I learned the hard way that muting everything is best when just sharing a terminal in over-the-shoulder mode. For more on that and other lessons, see my perpetually unpublished <a href="https://rwxrob.github.io/books/bad-strimmer/"><em>Bad Strimmer</em></a>.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_why_asciidoc_instead_of_mkdocs"><a class="link" href="#_why_asciidoc_instead_of_mkdocs">Why AsciiDoc instead of MkDocs?</a></h2>
<div class="sectionbody">
<div class="paragraph">
Expand All @@ -1673,7 +1682,7 @@ <h2 id="_why_asciidoc_instead_of_mkdocs"><a class="link" href="#_why_asciidoc_in
</div>
<div id="footer">
<div id="footer-text">
Last updated 2024-12-19 12:58:55 -0500
Last updated 2024-12-19 14:06:25 -0500
</div>
</div>
</body>
Expand Down

0 comments on commit 6b4c5c4

Please sign in to comment.