Skip to content
Snippets Groups Projects
Commit 05558cdf authored by Ophestra's avatar Ophestra
Browse files

sessions, config, basic handling of telegram side of things

parent f1d7f36f
Branches
No related tags found
No related merge requests found
bridgething
bridge.conf
*.swp
.idea/
\ No newline at end of file
package main
import "log"
func cleanup() {
if botAPI != nil {
botAPI.StopReceivingUpdates()
}
if session != nil {
if err := session.Close(); err != nil {
log.Printf("error closing discord session: %s", err)
}
}
}
config.go 0 → 100644
package main
import (
"flag"
"github.com/pelletier/go-toml/v2"
"log"
"os"
"strconv"
)
var (
config conf
confPath string
parsed = false
discordBridge = make(map[string]bridgeConf)
telegramBridge = make(map[int64]bridgeConf)
)
func init() {
flag.StringVar(&confPath, "c", "bridge.conf", "specify location of configuration file")
}
type conf struct {
System systemConf
Telegram telegramConf
Discord discordConf
Bridges struct{ Item []bridgeConf }
}
type systemConf struct {
Verbose bool
DisplaceHeader bool
}
type telegramConf struct {
Token string
NameFormat string
StickerEmoji bool
BypassBacklog bool
}
type discordConf struct {
Token string
NameFormat string
StickerText bool
}
type bridgeConf struct {
Label string
Discord bridgePlatformConf
Telegram bridgePlatformConf
}
type bridgePlatformConf struct {
ID int
Enable bool
Join bool
Leave bool
Delete bool
}
func setupConfig() {
if parsed {
panic("config already parsed")
}
defer func() { parsed = true }()
if file, err := os.Open(confPath); err != nil {
if !os.IsNotExist(err) {
log.Fatalf("error opening configuration file: %s", err)
}
if file, err = os.Create(confPath); err != nil {
log.Fatalf("error creating configuration file: %s", err)
} else {
encoder := toml.NewEncoder(file)
encoder.SetIndentSymbol(" ")
encoder.SetIndentTables(true)
if err = encoder.Encode(defConf); err != nil {
log.Fatalf("error generating default configuration: %s", err)
} else {
log.Print("default configuration generated")
os.Exit(1)
}
}
} else if err = toml.NewDecoder(file).Decode(&config); err != nil {
log.Fatalf("error parsing configuration: %s", err)
} else {
if err = file.Close(); err != nil {
log.Fatalf("error closing configuration: %s", err)
}
}
for _, c := range config.Bridges.Item {
if c.Discord.Enable {
discordBridge[strconv.Itoa(c.Discord.ID)] = c
}
if c.Telegram.Enable {
telegramBridge[int64(c.Telegram.ID)] = c
}
}
}
var defConf = conf{
System: systemConf{
Verbose: false,
DisplaceHeader: true,
},
Telegram: telegramConf{
Token: "INSERT_TOKEN_HERE",
NameFormat: "**$FIRST$SPACE$LAST (@$USER)**",
StickerEmoji: true,
BypassBacklog: true,
},
Discord: discordConf{
Token: "INSERT_TOKEN_HERE",
NameFormat: "$USER#$DISCRIMINATOR",
StickerText: true,
},
Bridges: struct{ Item []bridgeConf }{Item: []bridgeConf{
{
Label: "Default",
Discord: bridgePlatformConf{
ID: 0,
Enable: true,
Join: true,
Leave: true,
Delete: true,
},
Telegram: bridgePlatformConf{
ID: 0,
Enable: true,
Join: true,
Leave: true,
Delete: true,
},
},
{
Label: "Disabled",
Discord: bridgePlatformConf{
ID: 0,
Enable: false,
Join: true,
Leave: true,
Delete: true,
},
Telegram: bridgePlatformConf{
ID: 0,
Enable: false,
Join: true,
Leave: true,
Delete: true,
},
},
}},
}
package main
import (
"github.com/bwmarrin/discordgo"
"log"
)
var session *discordgo.Session
func init() {
discordgo.Logger = func(msgL, _ int, format string, a ...interface{}) {
prefix := "discord: "
switch msgL {
case discordgo.LogError:
prefix += "[error] "
case discordgo.LogWarning:
prefix += "[warning] "
case discordgo.LogInformational:
prefix += "[informational] "
case discordgo.LogDebug:
if !config.System.Verbose {
return
}
prefix += "[debug] "
}
log.Printf(prefix+format, a...)
}
}
func openDiscord() {
if s, err := discordgo.New(); err != nil {
// this should never happen
log.Fatalf("error creating discord session: %s", err)
} else {
session = s
}
session.Identify.Intents = discordgo.IntentsGuildMessages
session.Token = "Bot " + config.Discord.Token
if err := session.Open(); err != nil {
log.Fatalf("error connecting to discord: %s", err)
} else {
log.Printf("connected to discord as %s#%s (%s)",
session.State.User.Username, session.State.User.Discriminator, session.State.User.ID)
}
ready <- struct{}{}
}
func handleDiscord() {
// TODO
}
module random.chars.jp/git/bridgething module random.chars.jp/git/bridgething
go 1.17 go 1.17
require (
github.com/bwmarrin/discordgo v0.23.2
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible
github.com/pelletier/go-toml/v2 v2.0.0-beta.3
)
require (
github.com/gorilla/websocket v1.4.0 // indirect
github.com/technoweenie/multipartstreamer v1.0.1 // indirect
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16 // indirect
)
go.sum 0 → 100644
github.com/bwmarrin/discordgo v0.23.2 h1:BzrtTktixGHIu9Tt7dEE6diysEF9HWnXeHuoJEt2fH4=
github.com/bwmarrin/discordgo v0.23.2/go.mod h1:c1WtWUGN6nREDmzIpyTp/iD3VYt4Fpx+bVyfBG7JE+M=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible h1:2cauKuaELYAEARXRkq2LrJ0yDDv1rW7+wrTEdVL3uaU=
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM=
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/pelletier/go-toml/v2 v2.0.0-beta.3 h1:PNCTU4naEJ8mKal97P3A2qDU74QRQGlv4FXiL1XDqi4=
github.com/pelletier/go-toml/v2 v2.0.0-beta.3/go.mod h1:aNseLYu/uKskg0zpr/kbr2z8yGuWtotWf/0BpGIAL2Y=
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/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.1-0.20210427113832-6241f9ab9942 h1:t0lM6y/M5IiUZyvbBTcngso8SZEZICH7is9B6g/obVU=
github.com/stretchr/testify v1.7.1-0.20210427113832-6241f9ab9942/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM=
github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16 h1:y6ce7gCWtnH+m3dCjzQ1PCuwl28DDIc3VNnvY29DlIA=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
main.go 0 → 100644
package main
import (
"flag"
"log"
"os"
"os/signal"
"syscall"
)
var (
exec string
rest bool
ready = make(chan struct{})
)
func main() {
flag.Parse()
setupConfig()
go openTelegram()
go openDiscord()
go func() {
<-ready
<-ready
log.Print("sessions ready, starting handlers")
go handleDiscord()
go handleTelegram()
}()
s := make(chan os.Signal, 1)
signal.Notify(s, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
func() {
defer func() { cleanup() }()
for {
sig := <-s
switch sig {
case syscall.SIGINT:
println()
log.Print("shutting down")
return
default:
log.Print("shutting down")
return
}
}
}()
if rest {
restart()
}
}
//go:build !windows
package main
import (
"log"
"os"
"syscall"
)
func restart() {
var err error
if exec == "" {
return
}
if _, err = os.Stat(exec); err != nil {
log.Fatalf("error stat exec: %s", err)
}
log.Printf("execve %s", exec)
err = syscall.Exec(exec, os.Args, os.Environ())
if err != nil {
log.Fatalf("error execve: %s", err)
}
}
package main
import (
"log"
"os"
)
func restart() {
if exec == "" {
return
}
if _, err := os.Stat(executable); err != nil {
log.Fatalf("error stat exec: %s", err)
}
wd, err := os.Getwd()
if err != nil {
log.Fatalf("error getwd: %s", err)
}
log.Printf("pwd is %s", wd)
if _, err = os.StartProcess(executable, []string{}, &os.ProcAttr{
Dir: wd,
Env: nil,
Files: []*os.File{os.Stderr, os.Stdin, os.Stdout},
Sys: nil,
}); err != nil {
log.Fatalf("error start process: %s", err)
}
}
package main
import (
"github.com/bwmarrin/discordgo"
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api"
"io"
"log"
"net/http"
"strconv"
"strings"
"time"
)
var botAPI *tgbotapi.BotAPI
type botLogger struct{}
func (botLogger) Printf(format string, v ...interface{}) {
log.Printf("telegram: "+format, v...)
}
func (botLogger) Println(v ...interface{}) {
log.Println(append([]interface{}{"telegram:"}, v...)...)
}
func init() {
if err := tgbotapi.SetLogger(botLogger{}); err != nil {
panic(err)
}
}
func openTelegram() {
if bot, err := tgbotapi.NewBotAPI(config.Telegram.Token); err != nil {
log.Fatalf("error connecting to telegram: %s", err)
} else {
botAPI = bot
}
log.Printf("connected to telegram as @%s (%v)", botAPI.Self.UserName, botAPI.Self.ID)
botAPI.Debug = config.System.Verbose
ready <- struct{}{}
}
func handleTelegram() {
u := tgbotapi.NewUpdate(0)
u.Timeout = 60
if updates, err := botAPI.GetUpdatesChan(u); err != nil {
log.Fatalf("error getting updates: %s", err)
} else {
if config.Telegram.BypassBacklog {
time.Sleep(time.Millisecond * 500)
updates.Clear()
}
for update := range updates {
respondTelegram(update)
}
}
}
var previousCaller = make(map[int64]int)
func setPreviousCaller(cid int64, uid int) bool {
if previousCaller[cid] == uid {
return true
}
previousCaller[cid] = uid
return false
}
func respondTelegram(update tgbotapi.Update) {
// TODO: cross reply
// TODO: sticker object storage
if update.Message == nil || update.Message.Chat == nil {
if update.EditedMessage != nil {
// TODO: handle edit
return
}
if config.System.Verbose {
log.Printf("got irrelevant update %v", update.UpdateID)
}
return
}
if update.Message.IsCommand() {
message := tgbotapi.NewMessage(update.Message.Chat.ID, "")
switch update.Message.Command() {
case "id":
message.Text = strconv.Itoa(int(update.Message.Chat.ID))
default:
}
if message.Text != "" {
message.ReplyToMessageID = update.Message.MessageID
if _, err := botAPI.Send(message); err != nil {
log.Printf("error responding to command %v, %s", update.Message.Chat.ID, err)
}
return
}
}
var (
dc bridgePlatformConf
tc bridgePlatformConf
)
if c, ok := telegramBridge[update.Message.Chat.ID]; !ok {
return
} else {
dc = c.Discord
tc = c.Telegram
}
var (
username = "unknown"
id = -1
dMsgID = "-1"
)
if update.Message.From != nil {
username = update.Message.From.UserName
id = update.Message.From.ID
}
space := ""
if update.Message.From.LastName != "" {
space = " "
}
name := ""
if !config.System.DisplaceHeader || !setPreviousCaller(update.Message.Chat.ID, update.Message.From.ID) {
// TODO: check the discord side for header displace
name = strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(config.Telegram.NameFormat,
"$FIRST", update.Message.From.FirstName),
"$LAST", update.Message.From.LastName),
"$USER", update.Message.From.UserName),
"$SPACE", space)
name += "\n"
}
if update.Message.Sticker != nil {
var url string
if u, err := botAPI.GetFileDirectURL(update.Message.Sticker.FileID); err != nil {
msg := tgbotapi.NewMessage(update.Message.Chat.ID, "error getting URL")
msg.ReplyToMessageID = update.Message.MessageID
log.Printf("error getting URL of %s: %s", update.Message.Sticker.FileID, err)
_, _ = botAPI.Send(msg)
return
} else {
url = u
}
var (
file *discordgo.File
body io.ReadCloser
)
if resp, err := http.Get(url); err != nil {
msg := tgbotapi.NewMessage(update.Message.Chat.ID, "error getting file")
msg.ReplyToMessageID = update.Message.MessageID
log.Printf("error getting file %s: %s", update.Message.Sticker.FileID, err)
_, _ = botAPI.Send(msg)
return
} else {
file = &discordgo.File{
Name: "sticker.webp",
ContentType: "image/webp",
Reader: resp.Body,
}
body = resp.Body
}
content := name
if config.Telegram.StickerEmoji {
content += update.Message.Sticker.Emoji
}
if message, err := session.ChannelMessageSendComplex(strconv.Itoa(dc.ID), &discordgo.MessageSend{
Content: content,
File: file,
}); err != nil {
msg := tgbotapi.NewMessage(update.Message.Chat.ID, "error relaying message")
msg.ReplyToMessageID = update.Message.MessageID
log.Printf("error relaying message %v: %s", update.Message.MessageID, err)
_, _ = botAPI.Send(msg)
return
} else {
dMsgID = message.ID
}
if err := body.Close(); err != nil {
log.Printf("error closing request body: %s", err)
}
log.Printf("T%vM%v -> D%vM%s @%s (%v): %s",
tc.ID, update.Message.MessageID, dc.ID, dMsgID, username, id, "Sticker: "+update.Message.Sticker.FileID)
return
}
if update.Message.Contact != nil {
// TODO: handle contact
return
}
if update.Message.Text == "" {
return
}
if message, err := session.ChannelMessageSend(strconv.Itoa(dc.ID), name+update.Message.Text); err != nil {
msg := tgbotapi.NewMessage(update.Message.Chat.ID, "error relaying message")
msg.ReplyToMessageID = update.Message.MessageID
log.Printf("error relaying message %v: %s", update.Message.MessageID, err.Error())
_, _ = botAPI.Send(msg)
} else {
dMsgID = message.ID
}
log.Printf("T%vM%v -> D%vM%s @%s (%v): %s",
tc.ID, update.Message.MessageID, dc.ID, dMsgID, username, id, update.Message.Text)
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment