make it better
authorEduardo <[email protected]>
Mon, 5 Feb 2024 17:58:28 +0000 (18:58 +0100)
committerEduardo <[email protected]>
Mon, 5 Feb 2024 17:58:28 +0000 (18:58 +0100)
.vscode/launch.json [new file with mode: 0644]
__debug_bin [new file with mode: 0755]
bot [new file with mode: 0755]
bot.go [new file with mode: 0644]
go.mod [new file with mode: 0644]
go.sum [new file with mode: 0644]

diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644 (file)
index 0000000..e6f70f0
--- /dev/null
@@ -0,0 +1,17 @@
+{
+    // Use IntelliSense to learn about possible attributes.
+    // Hover to view descriptions of existing attributes.
+    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "name": "Launch",
+            "type": "go",
+            "request": "launch",
+            "mode": "auto",
+            "program": "${workspaceFolder}/bot.go",
+            "env": {},
+            "args": []
+        },
+    ]
+}
\ No newline at end of file
diff --git a/__debug_bin b/__debug_bin
new file mode 100755 (executable)
index 0000000..e2c5e11
Binary files /dev/null and b/__debug_bin differ
diff --git a/bot b/bot
new file mode 100755 (executable)
index 0000000..989db54
Binary files /dev/null and b/bot differ
diff --git a/bot.go b/bot.go
new file mode 100644 (file)
index 0000000..19b6a50
--- /dev/null
+++ b/bot.go
@@ -0,0 +1,373 @@
+package main
+
+import (
+       "bufio"
+       "context"
+       "log"
+       "os"
+       "regexp"
+       "sort"
+       "strconv"
+       "strings"
+
+       "github.com/bwmarrin/discordgo"
+       tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
+)
+
+var (
+       TgApiToken   = "6444047266:AAH86vKKDeMktgpqDIwWElH_s9nu-ZgqdB8"
+       DiscApiToken = "MTIwMzg0MzAyMDg5ODgzMjM5NA.G6e3Eo.yU90uQzHOMCw-cHguOhzwlXhnIcHo5YId60U64"
+
+       UpdateTextBtn  = "Update"
+       InitBotText    = "I'm new here, I need to update my text. Click the button below to update it."
+       menuTextMarkup = tgbotapi.NewInlineKeyboardMarkup(
+               tgbotapi.NewInlineKeyboardRow(
+                       tgbotapi.NewInlineKeyboardButtonData(UpdateTextBtn, UpdateTextBtn),
+               ),
+       )
+
+       telegram *tgbotapi.BotAPI
+
+       // bot msg
+       botMsg *tgbotapi.Message
+
+       // bot text message
+       botText string = InitBotText
+
+       Members []*discordgo.Member
+
+       // List of users online
+       usersOnline []string
+
+       // List of users in voice chat
+       usersInVoiceChat []string
+)
+
+func main() {
+       var err error
+       telegram, err = tgbotapi.NewBotAPI(TgApiToken)
+       if err != nil {
+               // Abort if something is wrong
+               log.Panic(err)
+       }
+
+       // Set this to true to log all interactions with telegram servers
+       telegram.Debug = false
+
+       u := tgbotapi.NewUpdate(0)
+       u.Timeout = 60
+
+       // Create a new cancellable background context. Calling `cancel()` leads to the cancellation of the context
+       ctx := context.Background()
+       ctx, cancel := context.WithCancel(ctx)
+
+       // `updates` is a golang channel which receives telegram updates
+       updates := telegram.GetUpdatesChan(u)
+
+       // Pass cancellable context to goroutine
+       go receiveUpdates(ctx, updates)
+
+       // Tell the user the bot is online
+       log.Println("Telegram Bot ready and listening for updates.")
+
+       // now start discord stuff
+       discord, err := discordgo.New("Bot " + DiscApiToken)
+       if err != nil {
+               log.Println("error creating Discord session,", err)
+               return
+       }
+       discord.LogLevel = discordgo.LogDebug
+       discord.AddHandlerOnce(ready)
+       discord.AddHandlerOnce(guildCreated)
+       discord.AddHandler(voiceChatUpdate)
+       discord.AddHandler(guildUpdates)
+
+       discord.Identify.Intents = discordgo.IntentGuilds | discordgo.IntentGuildPresences | discordgo.IntentGuildVoiceStates
+
+       // Open a websocket connection to Discord and begin listening.
+       err = discord.Open()
+       if err != nil {
+               log.Println("error opening discord session: ", err)
+       }
+
+       // Tell the user the bot is online
+       log.Println("Discord Bot ready.")
+
+       log.Println("Press enter to stop")
+       // Wait for a newline symbol, then cancel handling updates
+       bufio.NewReader(os.Stdin).ReadBytes('\n')
+       cancel()
+
+}
+
+func receiveUpdates(ctx context.Context, updates tgbotapi.UpdatesChannel) {
+       // `for {` means the loop is infinite until we manually stop it
+       for {
+               select {
+               // stop looping if ctx is cancelled
+               case <-ctx.Done():
+                       return
+               // receive update from channel and then handle it
+               case update := <-updates:
+                       handleUpdate(update)
+               }
+       }
+}
+
+func handleUpdate(update tgbotapi.Update) {
+       switch {
+       // Handle messages
+       case update.Message != nil:
+               handleMessage(update.Message)
+
+       // handle buttons click
+       case update.CallbackQuery != nil:
+               handleButton(update.CallbackQuery)
+       }
+}
+
+func handleMessage(message *tgbotapi.Message) {
+       user := message.From
+       text := message.Text
+
+       if user == nil {
+               return
+       }
+
+       // Print to console
+       log.Printf("%s wrote %s", user.FirstName, text)
+
+       var err error
+       if strings.HasPrefix(text, "/") {
+               err = handleCommand(message.Chat.ID, text)
+       }
+       // else {
+       //      // This is equivalent to forwarding, without the sender's name
+       //      // copyMsg := tgbotapi.NewCopyMessage(message.Chat.ID, message.Chat.ID, message.MessageID)
+       //      // _, err = telegram.CopyMessage(copyMsg)
+       // }
+
+       if err != nil {
+               log.Printf("An error occured: %s", err.Error())
+       }
+}
+
+// When we get a command, we react accordingly
+func handleCommand(chatId int64, command string) error {
+       var err error
+
+       switch command {
+       case "/start":
+               err = sendMenu(chatId)
+       }
+
+       return err
+}
+
+func sendMenu(chatId int64) error {
+       msg := tgbotapi.NewMessage(chatId, botText)
+       msg.ParseMode = tgbotapi.ModeHTML
+       msg.ReplyMarkup = menuTextMarkup
+       msg.DisableNotification = true
+       resMsg, err := telegram.Send(msg)
+       botMsg = &resMsg
+       return err
+}
+
+func handleButton(query *tgbotapi.CallbackQuery) {
+       var text string
+
+       markup := tgbotapi.NewInlineKeyboardMarkup()
+       message := query.Message
+
+       if botMsg == nil {
+               botMsg = message
+       }
+
+       if query.Data == UpdateTextBtn {
+               text = botText
+               markup = menuTextMarkup
+       }
+
+       callbackCfg := tgbotapi.NewCallback(query.ID, "")
+       telegram.Send(callbackCfg)
+
+       // Replace menu text and keyboard
+       msg := tgbotapi.NewEditMessageTextAndMarkup(message.Chat.ID, message.MessageID, text, markup)
+       msg.ParseMode = tgbotapi.ModeHTML
+       telegram.Send(msg)
+}
+
+//region discord bot stuff
+
+// This function will be called (due to AddHandler above) when the bot receives
+// the "ready" event from Discord.
+func ready(s *discordgo.Session, event *discordgo.Ready) {
+       // Set the playing status.
+       s.UpdateListeningStatus("te a ti")
+}
+
+func voiceChatUpdate(s *discordgo.Session, m *discordgo.VoiceStateUpdate) {
+       if m.ChannelID != "" {
+               // The user joined a channel, moved to a new channel, or edited its status
+               usersInVoiceChat = append(removeMemberFromSlice(usersInVoiceChat, *m.Member), formatUserVoiceStatus(*m.Member, *m.VoiceState))
+       } else {
+               // The user left a channel
+               usersInVoiceChat = removeMemberFromSlice(usersInVoiceChat, *m.Member)
+       }
+
+       formatBotMessage()
+}
+
+// The guild was created (or the bot was added to a new guild, or the bot was restarted)
+func guildCreated(s *discordgo.Session, m *discordgo.GuildCreate) {
+       // update the list of users online
+       for _, presence := range m.Presences {
+               if presence.User != nil && m.Members != nil && presence.Status != discordgo.StatusOffline && presence.Status != discordgo.StatusInvisible {
+                       Members = m.Members
+                       var member = getMemberFromMembers(m.Members, presence.User.ID)
+                       if member.User.Bot {
+                               continue
+                       }
+                       usersOnline = append(usersOnline, formatUserStatus(*member, presence.Status))
+               }
+       }
+
+       // update the list of users in voice chat
+       for _, vs := range m.VoiceStates {
+               if vs.UserID != "" {
+                       var member = getMemberFromMembers(m.Members, vs.UserID)
+                       if member != nil {
+                               usersInVoiceChat = append(usersInVoiceChat, formatUserVoiceStatus(*member, *vs))
+                       }
+               }
+       }
+
+       formatBotMessage()
+}
+
+// The guild was updated (an user did something or changed status)
+func guildUpdates(s *discordgo.Session, m *discordgo.PresenceUpdate) {
+       if m.Presence.User != nil {
+               var member = getMemberFromMembers(Members, m.Presence.User.ID)
+               if m.Presence.Status != discordgo.StatusOffline && m.Presence.Status != discordgo.StatusInvisible {
+                       usersOnline = append(removeMemberFromSlice(usersOnline, *member), formatUserStatus(*member, m.Presence.Status))
+               } else {
+                       usersOnline = removeMemberFromSlice(usersOnline, *member)
+               }
+       }
+       formatBotMessage()
+}
+
+func formatBotMessage() {
+       log.Println("formatting bot message...")
+       botText = ""
+       re := regexp.MustCompile(`\\\[[0-9]+\]`) // remove user id from the string
+
+       if len(usersOnline) > 0 {
+               botText += "πŸ‘€ <b>EN LÍNEA (" + strconv.Itoa(len(usersOnline)) + ")</b>" + "\n"
+
+               sort.Strings(usersOnline)
+               for _, user := range usersOnline {
+                       user = re.ReplaceAllString(user, "")
+                       botText += user + "\n"
+               }
+       }
+
+       if len(usersInVoiceChat) > 0 {
+               botText += "\n" + "πŸŽ™ <b>CHAT DE VOZ (" + strconv.Itoa(len(usersInVoiceChat)) + ")</b>" + "\n"
+
+               sort.Strings(usersInVoiceChat)
+               for _, user := range usersInVoiceChat {
+                       user = re.ReplaceAllString(user, "")
+                       botText += user + "\n"
+               }
+       }
+
+       if botText == "" {
+               botText = "πŸ’€ Parece que no hay nadie conectado."
+       }
+
+       log.Println(botText)
+
+       if botMsg != nil {
+               msg := tgbotapi.NewEditMessageTextAndMarkup(botMsg.Chat.ID, botMsg.MessageID, botText, menuTextMarkup)
+               msg.ParseMode = tgbotapi.ModeHTML
+               telegram.Send(msg)
+       }
+}
+
+func formatUserStatus(member discordgo.Member, status discordgo.Status) string {
+       var emoji string
+
+       switch status {
+       case discordgo.StatusOnline:
+               emoji = "🟒"
+       case discordgo.StatusIdle:
+               emoji = "🟑"
+       case discordgo.StatusDoNotDisturb:
+               emoji = "πŸ”΄"
+       }
+
+       return emoji + " " + formatUserVisibleName(member)
+}
+
+func formatUserVoiceStatus(member discordgo.Member, voiceState discordgo.VoiceState) string {
+       var emoji string
+
+       emoji = "🐡"
+       if member.Mute || voiceState.Mute || voiceState.SelfMute {
+               emoji = "πŸ™Š"
+       }
+       if member.Deaf || voiceState.Deaf || voiceState.SelfDeaf {
+               emoji = "πŸ™‰"
+       }
+       if voiceState.SelfVideo {
+               emoji += "πŸ“Ή"
+       }
+       if voiceState.SelfStream {
+               emoji += "πŸ–₯"
+       }
+
+       emoji += " "
+
+       return emoji + formatUserVisibleName(member)
+}
+
+func formatUserVisibleName(member discordgo.Member) string {
+       var user string
+       var id = member.User.ID
+
+       if member.User.DisplayName != "" {
+               user = member.User.DisplayName
+       } else {
+               user = member.User.Username
+       }
+
+       if member.Nick != "" {
+               user = member.Nick + " (" + user + ")"
+       }
+
+       return user + "\\[" + id + "]"
+}
+
+func removeMemberFromSlice(slice []string, member discordgo.Member) []string {
+       var id = "\\[" + member.User.ID + "]"
+
+       for i, v := range slice {
+               if strings.Contains(v, id) {
+                       return append(slice[:i], slice[i+1:]...)
+               }
+       }
+
+       return slice
+}
+
+func getMemberFromMembers(slice []*discordgo.Member, id string) *discordgo.Member {
+       for _, v := range slice {
+               if v.User.ID == id {
+                       return v
+               }
+       }
+       return nil
+}
diff --git a/go.mod b/go.mod
new file mode 100644 (file)
index 0000000..7c87ada
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,15 @@
+module edufdez.es/DiscordPresence2TelegramBot
+
+go 1.18
+
+require (
+       github.com/bwmarrin/discordgo v0.27.1
+       github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1
+       github.com/gorilla/websocket v1.5.1 // indirect
+)
+
+require (
+       golang.org/x/crypto v0.18.0 // indirect
+       golang.org/x/net v0.20.0 // indirect
+       golang.org/x/sys v0.16.0 // indirect
+)
diff --git a/go.sum b/go.sum
new file mode 100644 (file)
index 0000000..f488647
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,19 @@
+github.com/bwmarrin/discordgo v0.27.1 h1:ib9AIc/dom1E/fSIulrBwnez0CToJE113ZGt4HoliGY=
+github.com/bwmarrin/discordgo v0.27.1/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY=
+github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 h1:wG8n/XJQ07TmjbITcGiUaOtXxdrINDz1b0J1w0SzqDc=
+github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1/go.mod h1:A2S0CWkNylc2phvKXWBBdD3K0iGnDBGbzRpISP2zBl8=
+github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
+github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
+golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
+golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
+golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
+golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
+golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=