From: Eduardo Date: Mon, 5 Feb 2024 17:58:28 +0000 (+0100) Subject: make it better X-Git-Url: http://git.edufdez.es/?a=commitdiff_plain;h=2ee3b65d0fa35511a59ed752d2d527da5dc94d89;p=sylphiette-bot.git make it better --- 2ee3b65d0fa35511a59ed752d2d527da5dc94d89 diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..e6f70f0 --- /dev/null +++ b/.vscode/launch.json @@ -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 index 0000000..e2c5e11 Binary files /dev/null and b/__debug_bin differ diff --git a/bot b/bot new file mode 100755 index 0000000..989db54 Binary files /dev/null and b/bot differ diff --git a/bot.go b/bot.go new file mode 100644 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 += "👤 EN LÍNEA (" + strconv.Itoa(len(usersOnline)) + ")" + "\n" + + sort.Strings(usersOnline) + for _, user := range usersOnline { + user = re.ReplaceAllString(user, "") + botText += user + "\n" + } + } + + if len(usersInVoiceChat) > 0 { + botText += "\n" + "🎙 CHAT DE VOZ (" + strconv.Itoa(len(usersInVoiceChat)) + ")" + "\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 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 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=