MOVE STUFF AROUND OH YEAH LIKE THAT
This commit is contained in:
parent
b29294fcbf
commit
8551fbbe2c
31 changed files with 51 additions and 955 deletions
10
Makefile
10
Makefile
|
@ -1,4 +1,4 @@
|
||||||
all: clessy-broker clessy-mods clessy-stats
|
all: tg-broker clessy-mods clessy-stats
|
||||||
|
|
||||||
deps:
|
deps:
|
||||||
go get github.com/boltdb/bolt/...
|
go get github.com/boltdb/bolt/...
|
||||||
|
@ -8,10 +8,10 @@ deps:
|
||||||
go get github.com/disintegration/imaging
|
go get github.com/disintegration/imaging
|
||||||
|
|
||||||
install-tg:
|
install-tg:
|
||||||
go install github.com/hamcha/clessy/tg
|
go get -u github.com/hamcha/tg
|
||||||
|
|
||||||
clessy-broker: install-tg
|
tg-broker: install-tg
|
||||||
go build -o clessy-broker github.com/hamcha/clessy/broker
|
go get -u github.com/hamcha/tg/cmd/tg-broker
|
||||||
|
|
||||||
clessy-mods: install-tg
|
clessy-mods: install-tg
|
||||||
go build -o clessy-mods github.com/hamcha/clessy/mods
|
go build -o clessy-mods github.com/hamcha/clessy/mods
|
||||||
|
@ -23,4 +23,4 @@ clessy-stats-import: install-tg
|
||||||
go build -o clessy-stats-import github.com/hamcha/clessy/stats-import
|
go build -o clessy-stats-import github.com/hamcha/clessy/stats-import
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f clessy-broker clessy-mods clessy-stats
|
rm -f clessy-mods clessy-stats
|
|
@ -1,27 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/hamcha/clessy/tg"
|
|
||||||
)
|
|
||||||
|
|
||||||
func executeClientCommand(action tg.ClientCommand, client net.Conn) {
|
|
||||||
switch action.Type {
|
|
||||||
case tg.CmdSendTextMessage:
|
|
||||||
data := *(action.TextMessageData)
|
|
||||||
api.SendTextMessage(data)
|
|
||||||
case tg.CmdGetFile:
|
|
||||||
data := *(action.FileRequestData)
|
|
||||||
api.GetFile(data, client, *action.Callback)
|
|
||||||
case tg.CmdSendPhoto:
|
|
||||||
data := *(action.PhotoData)
|
|
||||||
api.SendPhoto(data)
|
|
||||||
case tg.CmdForwardMessage:
|
|
||||||
data := *(action.ForwardMessageData)
|
|
||||||
api.ForwardMessage(data)
|
|
||||||
case tg.CmdSendChatAction:
|
|
||||||
data := *(action.ChatActionData)
|
|
||||||
api.SendChatAction(data)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,79 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/hamcha/clessy/tg"
|
|
||||||
)
|
|
||||||
|
|
||||||
var clients []net.Conn
|
|
||||||
|
|
||||||
func startClientsServer(bind string) {
|
|
||||||
listener, err := net.Listen("tcp", bind)
|
|
||||||
assert(err)
|
|
||||||
|
|
||||||
// Accept loop
|
|
||||||
for {
|
|
||||||
c, err := listener.Accept()
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Can't accept client: %s\n", err.Error())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
clients = append(clients, c)
|
|
||||||
go handleClient(c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleClient(c net.Conn) {
|
|
||||||
b := bufio.NewReader(c)
|
|
||||||
defer c.Close()
|
|
||||||
|
|
||||||
// Start reading messages
|
|
||||||
buf := make([]byte, 0)
|
|
||||||
for {
|
|
||||||
bytes, isPrefix, err := b.ReadLine()
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
buf = append(buf, bytes...)
|
|
||||||
if isPrefix {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get command
|
|
||||||
var cmd tg.ClientCommand
|
|
||||||
err = json.Unmarshal(buf, &cmd)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("[handleClient] Can't parse JSON: %s\r\n", err.Error())
|
|
||||||
log.Printf("%s\n", string(buf))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Empty buffer
|
|
||||||
buf = []byte{}
|
|
||||||
|
|
||||||
executeClientCommand(cmd, c)
|
|
||||||
}
|
|
||||||
removeCon(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeCon(c net.Conn) {
|
|
||||||
for i, con := range clients {
|
|
||||||
if c == con {
|
|
||||||
clients = append(clients[:i], clients[i+1:]...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func broadcast(message string) {
|
|
||||||
for _, c := range clients {
|
|
||||||
_, err := fmt.Fprintln(c, message)
|
|
||||||
if err != nil {
|
|
||||||
removeCon(c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,57 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"flag"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
// The Config data (parsed from JSON)
|
|
||||||
type Config struct {
|
|
||||||
BindServer string /* Address:Port to bind for Telegram */
|
|
||||||
BindClients string /* Address:Port to bind for clients */
|
|
||||||
Token string /* Telegram bot token */
|
|
||||||
BaseURL string /* Base URL for webhook */
|
|
||||||
WebhookURL string /* Webhook URL */
|
|
||||||
}
|
|
||||||
|
|
||||||
func assert(err error) {
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var api *Telegram
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
cfgpath := flag.String("config", "config.json", "Path to configuration file")
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
file, err := os.Open(*cfgpath)
|
|
||||||
assert(err)
|
|
||||||
|
|
||||||
var config Config
|
|
||||||
err = json.NewDecoder(file).Decode(&config)
|
|
||||||
assert(err)
|
|
||||||
|
|
||||||
// Create Telegram API object
|
|
||||||
api = mkAPI(config.Token)
|
|
||||||
|
|
||||||
// Setup webhook handler
|
|
||||||
go func() {
|
|
||||||
log.Println("Starting webserver..")
|
|
||||||
http.HandleFunc(config.WebhookURL, webhook)
|
|
||||||
err := http.ListenAndServe(config.BindServer, nil)
|
|
||||||
assert(err)
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Register webhook @ Telegram
|
|
||||||
log.Println("Registering webhook..")
|
|
||||||
api.SetWebhook(config.BaseURL + config.WebhookURL)
|
|
||||||
|
|
||||||
// Create server for clients
|
|
||||||
log.Println("Starting clients server..")
|
|
||||||
startClientsServer(config.BindClients)
|
|
||||||
}
|
|
|
@ -1,220 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"mime/multipart"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/hamcha/clessy/tg"
|
|
||||||
)
|
|
||||||
|
|
||||||
// APIEndpoint is Telegram's current Bot API base url endpoint
|
|
||||||
const APIEndpoint = "https://api.telegram.org/"
|
|
||||||
|
|
||||||
// Telegram is the API client for the Telegram Bot API
|
|
||||||
type Telegram struct {
|
|
||||||
Token string
|
|
||||||
}
|
|
||||||
|
|
||||||
// mkAPI creates a Telegram instance from a Bot API token
|
|
||||||
func mkAPI(token string) *Telegram {
|
|
||||||
tg := new(Telegram)
|
|
||||||
tg.Token = token
|
|
||||||
return tg
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetWebhook sets the webhook address so that Telegram knows where to send updates
|
|
||||||
func (t Telegram) SetWebhook(webhook string) {
|
|
||||||
resp, err := http.PostForm(t.apiURL("setWebhook"), url.Values{"url": {webhook}})
|
|
||||||
if !checkerr("SetWebhook/http.PostForm", err) {
|
|
||||||
defer resp.Body.Close()
|
|
||||||
var result tg.APIResponse
|
|
||||||
err = json.NewDecoder(resp.Body).Decode(&result)
|
|
||||||
if err != nil {
|
|
||||||
log.Println("[SetWebhook] Could not read reply: " + err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if result.Ok {
|
|
||||||
log.Println("Webhook successfully set!")
|
|
||||||
} else {
|
|
||||||
log.Printf("[SetWebhook] Error setting webhook (errcode %d): %s\n", *(result.ErrCode), *(result.Description))
|
|
||||||
panic(errors.New("Cannot set webhook"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SendTextMessage sends an HTML-styled text message to a specified chat
|
|
||||||
func (t Telegram) SendTextMessage(data tg.ClientTextMessageData) {
|
|
||||||
postdata := url.Values{
|
|
||||||
"chat_id": {strconv.FormatInt(data.ChatID, 10)},
|
|
||||||
"text": {data.Text},
|
|
||||||
"parse_mode": {"HTML"},
|
|
||||||
}
|
|
||||||
if data.ReplyID != nil {
|
|
||||||
postdata["reply_to_message_id"] = []string{strconv.FormatInt(*(data.ReplyID), 10)}
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := http.PostForm(t.apiURL("sendMessage"), postdata)
|
|
||||||
checkerr("SendTextMessage/http.PostForm", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t Telegram) SendPhoto(data tg.ClientPhotoData) {
|
|
||||||
// Decode photo from b64
|
|
||||||
photolen := base64.StdEncoding.DecodedLen(len(data.Bytes))
|
|
||||||
photobytes := make([]byte, photolen)
|
|
||||||
decoded, err := base64.StdEncoding.Decode(photobytes, []byte(data.Bytes))
|
|
||||||
if checkerr("SendPhoto/base64.Decode", err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write file into multipart buffer
|
|
||||||
body := new(bytes.Buffer)
|
|
||||||
writer := multipart.NewWriter(body)
|
|
||||||
part, err := writer.CreateFormFile("photo", data.Filename)
|
|
||||||
if checkerr("SendPhoto/multipart.CreateFormFile", err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
part.Write(photobytes[0:decoded])
|
|
||||||
|
|
||||||
// Write other fields
|
|
||||||
writer.WriteField("chat_id", strconv.FormatInt(data.ChatID, 10))
|
|
||||||
|
|
||||||
if data.ReplyID != nil {
|
|
||||||
writer.WriteField("reply_to_message_id", strconv.FormatInt(*data.ReplyID, 10))
|
|
||||||
}
|
|
||||||
|
|
||||||
if data.Caption != "" {
|
|
||||||
writer.WriteField("caption", data.Caption)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = writer.Close()
|
|
||||||
if checkerr("SendPhoto/writer.Close", err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create HTTP client and execute request
|
|
||||||
client := &http.Client{}
|
|
||||||
req, err := http.NewRequest("POST", t.apiURL("sendPhoto"), body)
|
|
||||||
if checkerr("SendPhoto/http.NewRequest", err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
req.Header.Add("Content-Type", writer.FormDataContentType())
|
|
||||||
|
|
||||||
_, err = client.Do(req)
|
|
||||||
checkerr("SendPhoto/http.Do", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t Telegram) ForwardMessage(data tg.ClientForwardMessageData) {
|
|
||||||
postdata := url.Values{
|
|
||||||
"chat_id": {strconv.FormatInt(data.ChatID, 10)},
|
|
||||||
"from_chat_id": {strconv.FormatInt(data.FromChatID, 10)},
|
|
||||||
"message_id": {strconv.FormatInt(data.MessageID, 10)},
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := http.PostForm(t.apiURL("forwardMessage"), postdata)
|
|
||||||
checkerr("ForwardMessage/http.PostForm", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t Telegram) SendChatAction(data tg.ClientChatActionData) {
|
|
||||||
postdata := url.Values{
|
|
||||||
"chat_id": {strconv.FormatInt(data.ChatID, 10)},
|
|
||||||
"action": {string(data.Action)},
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := http.PostForm(t.apiURL("sendChatAction"), postdata)
|
|
||||||
checkerr("SendChatAction/http.PostForm", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetFile sends a "getFile" API call to Telegram's servers and fetches the file
|
|
||||||
// specified afterward. The file will be then send back to the client that requested it
|
|
||||||
// with the specified callback id.
|
|
||||||
func (t Telegram) GetFile(data tg.FileRequestData, client net.Conn, callback int) {
|
|
||||||
fail := func(msg string) {
|
|
||||||
errmsg, _ := json.Marshal(tg.BrokerUpdate{
|
|
||||||
Type: tg.BError,
|
|
||||||
Error: &msg,
|
|
||||||
Callback: &callback,
|
|
||||||
})
|
|
||||||
fmt.Fprintln(client, string(errmsg))
|
|
||||||
}
|
|
||||||
|
|
||||||
postdata := url.Values{
|
|
||||||
"file_id": {data.FileID},
|
|
||||||
}
|
|
||||||
resp, err := http.PostForm(t.apiURL("getFile"), postdata)
|
|
||||||
if checkerr("GetFile/post", err) {
|
|
||||||
fail("Server didn't like my request")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
var filespecs = struct {
|
|
||||||
Ok bool `json:"ok"`
|
|
||||||
Result *tg.APIFile `json:"result,omitempty"`
|
|
||||||
}{}
|
|
||||||
err = json.NewDecoder(resp.Body).Decode(&filespecs)
|
|
||||||
if checkerr("GetFile/json.Decode", err) {
|
|
||||||
fail("Server sent garbage (or error)")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if filespecs.Result == nil {
|
|
||||||
fail("Server didn't send a file info, does the file exist?")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
result := *filespecs.Result
|
|
||||||
|
|
||||||
path := APIEndpoint + "file/bot" + t.Token + "/" + *result.Path
|
|
||||||
fileresp, err := http.Get(path)
|
|
||||||
if checkerr("GetFile/get", err) {
|
|
||||||
fail("Could not retrieve file from Telegram's servers")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer fileresp.Body.Close()
|
|
||||||
|
|
||||||
rawdata, err := ioutil.ReadAll(fileresp.Body)
|
|
||||||
if checkerr("GetFile/ioutil.ReadAll", err) {
|
|
||||||
fail("Could not read file data")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
rawlen := len(rawdata)
|
|
||||||
if rawlen != *result.Size {
|
|
||||||
// ???
|
|
||||||
log.Printf("[GetFile] WARN ?? Downloaded file does not match provided filesize: %d != %d\n", rawlen, *result.Size)
|
|
||||||
}
|
|
||||||
b64data := base64.StdEncoding.EncodeToString(rawdata)
|
|
||||||
|
|
||||||
clientmsg, err := json.Marshal(tg.BrokerUpdate{
|
|
||||||
Type: tg.BFile,
|
|
||||||
Bytes: &b64data,
|
|
||||||
Callback: &callback,
|
|
||||||
})
|
|
||||||
if checkerr("GetFile/json.Marshal", err) {
|
|
||||||
fail("Could not serialize reply JSON")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintln(client, string(clientmsg))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t Telegram) apiURL(method string) string {
|
|
||||||
return APIEndpoint + "bot" + t.Token + "/" + method
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkerr(method string, err error) bool {
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("[%s] Error: %s\n", method, err.Error())
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/hamcha/clessy/tg"
|
|
||||||
)
|
|
||||||
|
|
||||||
func webhook(rw http.ResponseWriter, req *http.Request) {
|
|
||||||
defer req.Body.Close()
|
|
||||||
|
|
||||||
// Re-encode request to ensure conformity
|
|
||||||
var update tg.APIUpdate
|
|
||||||
err := json.NewDecoder(req.Body).Decode(&update)
|
|
||||||
if err != nil {
|
|
||||||
log.Println("[webhook] Received incorrect request: " + err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := json.Marshal(tg.BrokerUpdate{
|
|
||||||
Type: tg.BMessage,
|
|
||||||
Message: &(update.Message),
|
|
||||||
Callback: nil,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Println("[webhook] Cannot re-encode json (??) : " + err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
broadcast(string(data))
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
{
|
|
||||||
"BindServer" : ":7313",
|
|
||||||
"BindClients": "127.0.0.1:7314",
|
|
||||||
"Token" : "Bot token here",
|
|
||||||
"BaseURL" : "https://my.bot.host",
|
|
||||||
"WebhookURL" : "/secret_url_here"
|
|
||||||
}
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/hamcha/clessy/tg"
|
"github.com/hamcha/tg"
|
||||||
)
|
)
|
||||||
|
|
||||||
const KYMURL = "http://knowyourmeme.com/memes/it-s-finally-a-friday/photos/page/2"
|
const KYMURL = "http://knowyourmeme.com/memes/it-s-finally-a-friday/photos/page/2"
|
||||||
|
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/hamcha/clessy/tg"
|
"github.com/hamcha/tg"
|
||||||
)
|
)
|
||||||
|
|
||||||
var dantes_frasiacaso = []string{
|
var dantes_frasiacaso = []string{
|
21
mods-toupdate/roll.go
Normal file
21
mods-toupdate/roll.go
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/hamcha/tg"
|
||||||
|
)
|
||||||
|
|
||||||
|
func roll(broker *tg.Broker, update tg.APIMessage) {
|
||||||
|
if isCommand(update, "roll") {
|
||||||
|
parts := strings.Split(*(update.Text), " ")
|
||||||
|
if len(parts) < 2 {
|
||||||
|
broker.SendTextMessage(update.Chat, "<b>Sintassi</b>\n/roll <i>cosa1</i> <i>[cosa2]</i> ..\n\n<b>Cose supportate</b>\nNumero intero casuale (es. 300, 10-20, 34-100)\nLancio di dadi (es. d6, 2d20, 100d12+10)\nUtente casuale (\"user\")", &update.MessageID)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func rolltoken(token string) {
|
||||||
|
|
||||||
|
}
|
|
@ -11,7 +11,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/hamcha/clessy/tg"
|
"github.com/hamcha/tg"
|
||||||
)
|
)
|
||||||
|
|
||||||
type QRequest struct {
|
type QRequest struct {
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/hamcha/clessy/tg"
|
"github.com/hamcha/tg"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Macro struct {
|
type Macro struct {
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/hamcha/clessy/tg"
|
"github.com/hamcha/tg"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Mod struct {
|
type Mod struct {
|
||||||
|
@ -47,10 +47,12 @@ var mods = map[string]Mod{
|
||||||
OnInit: proverbio_init,
|
OnInit: proverbio_init,
|
||||||
OnMessage: proverbio_message,
|
OnMessage: proverbio_message,
|
||||||
},
|
},
|
||||||
|
/*
|
||||||
"talk": {
|
"talk": {
|
||||||
OnInit: talk_init,
|
OnInit: inittalk,
|
||||||
OnMessage: talk_message,
|
OnMessage: talk,
|
||||||
},
|
},
|
||||||
|
*/
|
||||||
"stt": {
|
"stt": {
|
||||||
OnInit: stt_init,
|
OnInit: stt_init,
|
||||||
OnMessage: stt_message,
|
OnMessage: stt_message,
|
||||||
|
|
|
@ -13,7 +13,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/golang/freetype"
|
"github.com/golang/freetype"
|
||||||
"github.com/hamcha/clessy/tg"
|
"github.com/hamcha/tg"
|
||||||
"github.com/llgcode/draw2d"
|
"github.com/llgcode/draw2d"
|
||||||
"github.com/llgcode/draw2d/draw2dimg"
|
"github.com/llgcode/draw2d/draw2dimg"
|
||||||
)
|
)
|
||||||
|
|
|
@ -3,7 +3,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
|
||||||
"github.com/hamcha/clessy/tg"
|
"github.com/hamcha/tg"
|
||||||
)
|
)
|
||||||
|
|
||||||
var metaactions = []string{
|
var metaactions = []string{
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
|
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
"github.com/hamcha/clessy/tg"
|
"github.com/hamcha/tg"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ProverbioData struct {
|
type ProverbioData struct {
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
"github.com/hamcha/clessy/tg"
|
"github.com/hamcha/tg"
|
||||||
)
|
)
|
||||||
|
|
||||||
var remindpath *string
|
var remindpath *string
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/hamcha/clessy/tg"
|
"github.com/hamcha/tg"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SearchResult struct {
|
type SearchResult struct {
|
||||||
|
|
|
@ -18,7 +18,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/golang/freetype"
|
"github.com/golang/freetype"
|
||||||
"github.com/hamcha/clessy/tg"
|
"github.com/hamcha/tg"
|
||||||
"github.com/llgcode/draw2d"
|
"github.com/llgcode/draw2d"
|
||||||
"github.com/llgcode/draw2d/draw2dimg"
|
"github.com/llgcode/draw2d/draw2dimg"
|
||||||
)
|
)
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
|
||||||
speech "cloud.google.com/go/speech/apiv1"
|
speech "cloud.google.com/go/speech/apiv1"
|
||||||
"github.com/hamcha/clessy/tg"
|
"github.com/hamcha/tg"
|
||||||
"google.golang.org/api/option"
|
"google.golang.org/api/option"
|
||||||
speechpb "google.golang.org/genproto/googleapis/cloud/speech/v1"
|
speechpb "google.golang.org/genproto/googleapis/cloud/speech/v1"
|
||||||
)
|
)
|
||||||
|
|
|
@ -13,7 +13,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/golang/freetype"
|
"github.com/golang/freetype"
|
||||||
"github.com/hamcha/clessy/tg"
|
"github.com/hamcha/tg"
|
||||||
"github.com/llgcode/draw2d"
|
"github.com/llgcode/draw2d"
|
||||||
"github.com/llgcode/draw2d/draw2dimg"
|
"github.com/llgcode/draw2d/draw2dimg"
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/hamcha/clessy/tg"
|
"github.com/hamcha/tg"
|
||||||
)
|
)
|
||||||
|
|
||||||
const viaggiurl = "http://free.rome2rio.com/api/1.2/json/Search?key=X5JMLHNc&languageCode=IT¤cyCode=EUR"
|
const viaggiurl = "http://free.rome2rio.com/api/1.2/json/Search?key=X5JMLHNc&languageCode=IT¤cyCode=EUR"
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/hamcha/clessy/tg"
|
"github.com/hamcha/tg"
|
||||||
)
|
)
|
||||||
|
|
||||||
func assert(err error) {
|
func assert(err error) {
|
||||||
|
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"flag"
|
"flag"
|
||||||
|
|
||||||
"github.com/boltdb/bolt"
|
"github.com/boltdb/bolt"
|
||||||
"github.com/hamcha/clessy/tg"
|
"github.com/hamcha/tg"
|
||||||
)
|
)
|
||||||
|
|
||||||
func assert(err error) {
|
func assert(err error) {
|
||||||
|
@ -42,6 +42,5 @@ func main() {
|
||||||
|
|
||||||
go startWebServer(*webBind)
|
go startWebServer(*webBind)
|
||||||
|
|
||||||
_, err = tg.CreateBrokerClient(*brokerAddr, process)
|
assert(tg.CreateBrokerClient(*brokerAddr, process))
|
||||||
assert(err)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/boltdb/bolt"
|
"github.com/boltdb/bolt"
|
||||||
"github.com/hamcha/clessy/tg"
|
"github.com/hamcha/tg"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/boltdb/bolt"
|
"github.com/boltdb/bolt"
|
||||||
"github.com/hamcha/clessy/tg"
|
"github.com/hamcha/tg"
|
||||||
)
|
)
|
||||||
|
|
||||||
var users map[string]string
|
var users map[string]string
|
||||||
|
|
154
tg/api.go
154
tg/api.go
|
@ -1,154 +0,0 @@
|
||||||
package tg
|
|
||||||
|
|
||||||
// APIUser represents the "User" JSON structure
|
|
||||||
type APIUser struct {
|
|
||||||
UserID int64 `json:"id"`
|
|
||||||
FirstName string `json:"first_name"`
|
|
||||||
LastName string `json:"last_name,omitempty"`
|
|
||||||
Username string `json:"username,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ChatType defines the type of chat
|
|
||||||
type ChatType string
|
|
||||||
|
|
||||||
const (
|
|
||||||
// ChatTypePrivate is a private chat (between user and bot)
|
|
||||||
ChatTypePrivate ChatType = "private"
|
|
||||||
|
|
||||||
// ChatTypeGroup is a group chat (<100 members)
|
|
||||||
ChatTypeGroup ChatType = "group"
|
|
||||||
|
|
||||||
// ChatTypeSupergroup is a supergroup chat (>=100 members)
|
|
||||||
ChatTypeSupergroup ChatType = "supergroup"
|
|
||||||
|
|
||||||
// ChatTypeChannel is a channel (Read-only)
|
|
||||||
ChatTypeChannel ChatType = "channel"
|
|
||||||
)
|
|
||||||
|
|
||||||
// APIChat represents the "Chat" JSON structure
|
|
||||||
type APIChat struct {
|
|
||||||
ChatID int64 `json:"id"`
|
|
||||||
Type ChatType `json:"type"`
|
|
||||||
Title *string `json:"title,omitempty"`
|
|
||||||
Username *string `json:"username,omitempty"`
|
|
||||||
FirstName *string `json:"first_name,omitempty"`
|
|
||||||
LastName *string `json:"last_name,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// APIMessage represents the "Message" JSON structure
|
|
||||||
type APIMessage struct {
|
|
||||||
MessageID int64 `json:"message_id"`
|
|
||||||
User APIUser `json:"from"`
|
|
||||||
Time int64 `json:"date"`
|
|
||||||
Chat *APIChat `json:"chat"`
|
|
||||||
FwdUser *APIUpdate `json:"forward_from,omitempty"`
|
|
||||||
FwdTime *int `json:"forward_date,omitempty"`
|
|
||||||
ReplyTo *APIMessage `json:"reply_to_message,omitempty"`
|
|
||||||
Text *string `json:"text,omitempty"`
|
|
||||||
Audio *APIAudio `json:"audio,omitempty"`
|
|
||||||
Document *APIDocument `json:"document,omitempty"`
|
|
||||||
Photo []APIPhotoSize `json:"photo,omitempty"`
|
|
||||||
Sticker *APISticker `json:"sticker,omitempty"`
|
|
||||||
Video *APIVideo `json:"video,omitempty"`
|
|
||||||
Voice *APIVoice `json:"voice,omitempty"`
|
|
||||||
Caption *string `json:"caption,omitempty"`
|
|
||||||
Contact *APIContact `json:"contact,omitempty"`
|
|
||||||
Location *APILocation `json:"location,omitempty"`
|
|
||||||
NewUser *APIUser `json:"new_chat_partecipant,omitempty"`
|
|
||||||
LeftUser *APIUser `json:"left_chat_partecipant,omitempty"`
|
|
||||||
PhotoDeleted *bool `json:"delete_chat_photo,omitempty"`
|
|
||||||
GroupCreated *bool `json:"group_chat_created,omitempty"`
|
|
||||||
SupergroupCreated *bool `json:"supergroup_chat_created,omitempty"`
|
|
||||||
ChannelCreated *bool `json:"channel_chat_created,omitempty"`
|
|
||||||
GroupToSuper *int64 `json:"migrate_to_chat_id,omitempty"`
|
|
||||||
GroupFromSuper *int64 `json:"migrate_from_chat_id,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// APIPhotoSize represents the "PhotoSize" JSON structure
|
|
||||||
type APIPhotoSize struct {
|
|
||||||
FileID string `json:"file_id"`
|
|
||||||
Width int `json:"width"`
|
|
||||||
Height int `json:"height"`
|
|
||||||
FileSize *int `json:"file_size,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// APIAudio represents the "Audio" JSON structure
|
|
||||||
type APIAudio struct {
|
|
||||||
FileID string `json:"file_id"`
|
|
||||||
Duration int `json:"duration"`
|
|
||||||
Performer *string `json:"performer,omitempty"`
|
|
||||||
Title *string `json:"title,omitempty"`
|
|
||||||
MimeType *string `json:"mime_type,omitempty"`
|
|
||||||
FileSize *int `json:"file_size,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// APIDocument represents the "Document" JSON structure
|
|
||||||
type APIDocument struct {
|
|
||||||
FileID string `json:"file_id"`
|
|
||||||
Thumbnail *APIPhotoSize `json:"thumb,omitempty"`
|
|
||||||
Filename string `json:"file_name"`
|
|
||||||
MimeType *string `json:"mime_type,omitempty"`
|
|
||||||
FileSize *int `json:"file_size,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// APISticker represents the "Sticker" JSON structure
|
|
||||||
type APISticker struct {
|
|
||||||
FileID string `json:"file_id"`
|
|
||||||
Width int `json:"width"`
|
|
||||||
Height int `json:"height"`
|
|
||||||
Thumbnail *APIPhotoSize `json:"thumb,omitempty"`
|
|
||||||
FileSize *int `json:"file_size,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// APIVideo represents the "Video" JSON structure
|
|
||||||
type APIVideo struct {
|
|
||||||
FileID string `json:"file_id"`
|
|
||||||
Width int `json:"width"`
|
|
||||||
Height int `json:"height"`
|
|
||||||
Duration int `json:"duration"`
|
|
||||||
Thumbnail *APIPhotoSize `json:"thumb,omitempty"`
|
|
||||||
MimeType *string `json:"mime_type,omitempty"`
|
|
||||||
FileSize *int `json:"file_size,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// APIVoice represents the "Voice" JSON structure
|
|
||||||
type APIVoice struct {
|
|
||||||
FileID string `json:"file_id"`
|
|
||||||
Duration int `json:"duration"`
|
|
||||||
MimeType *string `json:"mime_type,omitempty"`
|
|
||||||
FileSize *int `json:"file_size,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// APIContact represents the "Contact" JSON structure
|
|
||||||
type APIContact struct {
|
|
||||||
PhoneNumber string `json:"phone_number"`
|
|
||||||
FirstName string `json:"first_name"`
|
|
||||||
LastName *string `json:"last_name,omitempty"`
|
|
||||||
UserID *int64 `json:"user_id,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// APILocation represents the "Location" JSON structure
|
|
||||||
type APILocation struct {
|
|
||||||
Longitude float64 `json:"longitude"`
|
|
||||||
Latitude float64 `json:"latitude"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// APIUpdate represents the "Update" JSON structure
|
|
||||||
type APIUpdate struct {
|
|
||||||
UpdateID int64 `json:"update_id"`
|
|
||||||
Message APIMessage `json:"message"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// APIFile represents the "File" JSON structure
|
|
||||||
type APIFile struct {
|
|
||||||
FileID string `json:"file_id"`
|
|
||||||
Size *int `json:"file_size,omitempty"`
|
|
||||||
Path *string `json:"file_path,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// APIResponse represents a response from the Telegram API
|
|
||||||
type APIResponse struct {
|
|
||||||
Ok bool `json:"ok"`
|
|
||||||
ErrCode *int `json:"error_code,omitempty"`
|
|
||||||
Description *string `json:"description,omitempty"`
|
|
||||||
}
|
|
166
tg/broker.go
166
tg/broker.go
|
@ -1,166 +0,0 @@
|
||||||
package tg
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"net"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Broker is a broker connection handler with callback management functions
|
|
||||||
type Broker struct {
|
|
||||||
Socket net.Conn
|
|
||||||
Callbacks []BrokerCallback
|
|
||||||
|
|
||||||
cbFree int
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConnectToBroker creates a Broker connection
|
|
||||||
func ConnectToBroker(brokerAddr string) (*Broker, error) {
|
|
||||||
sock, err := net.Dial("tcp", brokerAddr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
broker := new(Broker)
|
|
||||||
broker.Socket = sock
|
|
||||||
broker.Callbacks = make([]BrokerCallback, 0)
|
|
||||||
broker.cbFree = 0
|
|
||||||
return broker, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close closes a broker connection
|
|
||||||
func (b *Broker) Close() {
|
|
||||||
b.Socket.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// SendTextMessage sends a HTML-styles text message to a chat.
|
|
||||||
// A reply_to message ID can be specified as optional parameter.
|
|
||||||
func (b *Broker) SendTextMessage(chat *APIChat, text string, original *int64) {
|
|
||||||
b.sendCmd(ClientCommand{
|
|
||||||
Type: CmdSendTextMessage,
|
|
||||||
TextMessageData: &ClientTextMessageData{
|
|
||||||
Text: text,
|
|
||||||
ChatID: chat.ChatID,
|
|
||||||
ReplyID: original,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// SendPhoto sends a photo with an optional caption to a chat.
|
|
||||||
// A reply_to message ID can be specified as optional parameter.
|
|
||||||
func (b *Broker) SendPhoto(chat *APIChat, data []byte, filename string, caption string, original *int64) {
|
|
||||||
b.sendCmd(ClientCommand{
|
|
||||||
Type: CmdSendPhoto,
|
|
||||||
PhotoData: &ClientPhotoData{
|
|
||||||
ChatID: chat.ChatID,
|
|
||||||
Filename: filename,
|
|
||||||
Bytes: base64.StdEncoding.EncodeToString(data),
|
|
||||||
Caption: caption,
|
|
||||||
ReplyID: original,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// ForwardMessage forwards a message between chats.
|
|
||||||
func (b *Broker) ForwardMessage(chat *APIChat, message APIMessage) {
|
|
||||||
b.sendCmd(ClientCommand{
|
|
||||||
Type: CmdForwardMessage,
|
|
||||||
ForwardMessageData: &ClientForwardMessageData{
|
|
||||||
ChatID: chat.ChatID,
|
|
||||||
FromChatID: message.Chat.ChatID,
|
|
||||||
MessageID: message.MessageID,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// SendChatAction sets a chat action for 5 seconds or less (canceled at first message sent)
|
|
||||||
func (b *Broker) SendChatAction(chat *APIChat, action ChatAction) {
|
|
||||||
b.sendCmd(ClientCommand{
|
|
||||||
Type: CmdSendChatAction,
|
|
||||||
ChatActionData: &ClientChatActionData{
|
|
||||||
ChatID: chat.ChatID,
|
|
||||||
Action: action,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetFile sends a file retrieval request to the Broker.
|
|
||||||
// This function is asynchronous as data will be delivered to the given callback.
|
|
||||||
func (b *Broker) GetFile(fileID string, fn BrokerCallback) int {
|
|
||||||
cid := b.RegisterCallback(fn)
|
|
||||||
b.sendCmd(ClientCommand{
|
|
||||||
Type: CmdGetFile,
|
|
||||||
FileRequestData: &FileRequestData{
|
|
||||||
FileID: fileID,
|
|
||||||
},
|
|
||||||
Callback: &cid,
|
|
||||||
})
|
|
||||||
return cid
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterCallback assigns a callback ID to the given callback and puts it on the callback list.
|
|
||||||
// This function should never be called by clients.
|
|
||||||
func (b *Broker) RegisterCallback(fn BrokerCallback) int {
|
|
||||||
cblen := len(b.Callbacks)
|
|
||||||
// List is full, append to end
|
|
||||||
if b.cbFree == cblen {
|
|
||||||
b.Callbacks = append(b.Callbacks, fn)
|
|
||||||
b.cbFree++
|
|
||||||
return cblen
|
|
||||||
}
|
|
||||||
// List is not full, use empty slot and find next one
|
|
||||||
id := b.cbFree
|
|
||||||
b.Callbacks[id] = fn
|
|
||||||
next := b.cbFree + 1
|
|
||||||
for ; next < cblen; next++ {
|
|
||||||
if b.Callbacks[next] == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
b.cbFree = next
|
|
||||||
return id
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveCallback removes a callback from the callback list by ID.
|
|
||||||
// This function should never be called by clients.
|
|
||||||
func (b *Broker) RemoveCallback(id int) {
|
|
||||||
b.Callbacks[id] = nil
|
|
||||||
if id < b.cbFree {
|
|
||||||
b.cbFree = id
|
|
||||||
}
|
|
||||||
b.resizeCbArray()
|
|
||||||
}
|
|
||||||
|
|
||||||
// SpliceCallback retrieves a callback by ID and removes it from the list.
|
|
||||||
// This function should never be called by clients.
|
|
||||||
func (b *Broker) SpliceCallback(id int) BrokerCallback {
|
|
||||||
defer b.RemoveCallback(id)
|
|
||||||
return b.Callbacks[id]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Broker) sendCmd(cmd ClientCommand) {
|
|
||||||
data, err := json.Marshal(cmd)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("[sendCmd] JSON Encode error: %s\n", err.Error())
|
|
||||||
}
|
|
||||||
fmt.Fprintln(b.Socket, string(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Broker) resizeCbArray() {
|
|
||||||
var i int
|
|
||||||
cut := false
|
|
||||||
for i = len(b.Callbacks); i > 0; i-- {
|
|
||||||
if b.Callbacks[i-1] != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
cut = true
|
|
||||||
}
|
|
||||||
if cut {
|
|
||||||
b.Callbacks = b.Callbacks[0:i]
|
|
||||||
if b.cbFree > i {
|
|
||||||
b.cbFree = i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
64
tg/client.go
64
tg/client.go
|
@ -1,64 +0,0 @@
|
||||||
package tg
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"encoding/json"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
)
|
|
||||||
|
|
||||||
// UpdateHandler is an update handler for webhook updates
|
|
||||||
type UpdateHandler func(broker *Broker, message APIMessage)
|
|
||||||
|
|
||||||
// BrokerCallback is a callback for broker responses to client requests
|
|
||||||
type BrokerCallback func(broker *Broker, update BrokerUpdate)
|
|
||||||
|
|
||||||
// CreateBrokerClient creates a connection to a broker and sends all webhook updates to a given function.
|
|
||||||
// This is the intended way to create clients, please refer to examples for how to make a simple client.
|
|
||||||
func CreateBrokerClient(brokerAddr string, updateFn UpdateHandler) error {
|
|
||||||
broker, err := ConnectToBroker(brokerAddr)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer broker.Close()
|
|
||||||
|
|
||||||
return RunBrokerClient(broker, updateFn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RunBrokerClient is a slimmer version of CreateBrokerClient for who wants to keep its own broker connection
|
|
||||||
func RunBrokerClient(broker *Broker, updateFn UpdateHandler) error {
|
|
||||||
in := bufio.NewReader(broker.Socket)
|
|
||||||
var buf []byte
|
|
||||||
for {
|
|
||||||
bytes, isPrefix, err := in.ReadLine()
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
buf = append(buf, bytes...)
|
|
||||||
|
|
||||||
if isPrefix {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
var update BrokerUpdate
|
|
||||||
err = json.Unmarshal(buf, &update)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("[tg - CreateBrokerClient] ERROR reading JSON: %s\r\n", err.Error())
|
|
||||||
log.Printf("%s\n", string(buf))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if update.Callback == nil {
|
|
||||||
// It's a generic message: dispatch to UpdateHandler
|
|
||||||
go updateFn(broker, *(update.Message))
|
|
||||||
} else {
|
|
||||||
// It's a response to a request: retrieve callback and call it
|
|
||||||
go broker.SpliceCallback(*(update.Callback))(broker, update)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Empty buffer
|
|
||||||
buf = []byte{}
|
|
||||||
}
|
|
||||||
|
|
||||||
return io.EOF
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
package tg_test
|
|
||||||
|
|
||||||
// This example creates a basic client that connects to a broker and checks for message containing greetings.
|
|
||||||
// If it finds a greeting message it will greet back the user (using the reply_to parameter)
|
|
||||||
func ExampleCreateBrokerClient() {
|
|
||||||
CreateBrokerClient("localhost:7314", func(broker *Broker, message APIMessage) {
|
|
||||||
// Check if it's a text message
|
|
||||||
if message.Text != nil {
|
|
||||||
// Check that it's a greeting
|
|
||||||
if *(message.Text) == "hello" || *(message.Text) == "hi" {
|
|
||||||
// Reply with a greeting!
|
|
||||||
broker.SendTextMessage(message.Chat, "Hello!", message.MessageID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
103
tg/command.go
103
tg/command.go
|
@ -1,103 +0,0 @@
|
||||||
package tg
|
|
||||||
|
|
||||||
// BrokerUpdateType distinguishes update types coming from the broker
|
|
||||||
type BrokerUpdateType string
|
|
||||||
|
|
||||||
const (
|
|
||||||
// BMessage is a message update (mostly webhook updates)
|
|
||||||
BMessage BrokerUpdateType = "message"
|
|
||||||
|
|
||||||
// BFile is a file retrieval response update
|
|
||||||
BFile BrokerUpdateType = "file"
|
|
||||||
|
|
||||||
// BError is an error the broker occurred while fulfilling a request
|
|
||||||
BError BrokerUpdateType = "error"
|
|
||||||
)
|
|
||||||
|
|
||||||
// BrokerUpdate is what is sent by the broker as update
|
|
||||||
type BrokerUpdate struct {
|
|
||||||
Type BrokerUpdateType
|
|
||||||
Callback *int `json:",omitempty"`
|
|
||||||
Error *string `json:",omitempty"`
|
|
||||||
Message *APIMessage `json:",omitempty"`
|
|
||||||
Bytes *string `json:",omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClientCommandType distinguishes requests sent by clients to the broker
|
|
||||||
type ClientCommandType string
|
|
||||||
|
|
||||||
const (
|
|
||||||
// CmdSendTextMessage requests the broker to send a text message to a chat
|
|
||||||
CmdSendTextMessage ClientCommandType = "sendText"
|
|
||||||
|
|
||||||
// CmdSendPhoto requests the broker to send a photo to a chat
|
|
||||||
CmdSendPhoto ClientCommandType = "sendPhoto"
|
|
||||||
|
|
||||||
// CmdForwardMessage requests the broker to forward a message between chats
|
|
||||||
CmdForwardMessage ClientCommandType = "forwardMessage"
|
|
||||||
|
|
||||||
// CmdGetFile requests the broker to get a file from Telegram
|
|
||||||
CmdGetFile ClientCommandType = "getFile"
|
|
||||||
|
|
||||||
// CmdSendChatAction requests the broker to set a chat action (typing, etc.)
|
|
||||||
CmdSendChatAction ClientCommandType = "sendChatAction"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ClientTextMessageData is the required data for a CmdSendTextMessage request
|
|
||||||
type ClientTextMessageData struct {
|
|
||||||
ChatID int64
|
|
||||||
Text string
|
|
||||||
ReplyID *int64 `json:",omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClientPhotoData is the required data for a CmdSendPhoto request
|
|
||||||
type ClientPhotoData struct {
|
|
||||||
ChatID int64
|
|
||||||
Bytes string
|
|
||||||
Filename string
|
|
||||||
Caption string `json:",omitempty"`
|
|
||||||
ReplyID *int64 `json:",omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClientForwardMessageData is the required data for a CmdForwardMessage request
|
|
||||||
type ClientForwardMessageData struct {
|
|
||||||
ChatID int64
|
|
||||||
FromChatID int64
|
|
||||||
MessageID int64
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClientChatActionData is the required data for a CmdSendChatAction request
|
|
||||||
type ClientChatActionData struct {
|
|
||||||
ChatID int64
|
|
||||||
Action ChatAction
|
|
||||||
}
|
|
||||||
|
|
||||||
// ChatAction is the action name for CmdSendChatAction requests
|
|
||||||
type ChatAction string
|
|
||||||
|
|
||||||
const (
|
|
||||||
ActionTyping ChatAction = "typing"
|
|
||||||
ActionUploadingPhoto ChatAction = "upload_photo"
|
|
||||||
ActionRecordingVideo ChatAction = "record_video"
|
|
||||||
ActionUploadingVideo ChatAction = "upload_video"
|
|
||||||
ActionRecordingAudio ChatAction = "record_audio"
|
|
||||||
ActionUploadingAudio ChatAction = "upload_audio"
|
|
||||||
ActionUploadingDocument ChatAction = "upload_document"
|
|
||||||
ActionFindingLocation ChatAction = "find_location"
|
|
||||||
)
|
|
||||||
|
|
||||||
// FileRequestData is the required data for a CmdGetFile request
|
|
||||||
type FileRequestData struct {
|
|
||||||
FileID string
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClientCommand is a request sent by clients to the broker
|
|
||||||
type ClientCommand struct {
|
|
||||||
Type ClientCommandType
|
|
||||||
TextMessageData *ClientTextMessageData `json:",omitempty"`
|
|
||||||
PhotoData *ClientPhotoData `json:",omitempty"`
|
|
||||||
ForwardMessageData *ClientForwardMessageData `json:",omitempty"`
|
|
||||||
ChatActionData *ClientChatActionData `json:",omitempty"`
|
|
||||||
FileRequestData *FileRequestData `json:",omitempty"`
|
|
||||||
Callback *int `json:",omitempty"`
|
|
||||||
}
|
|
Reference in a new issue