Implement getFile

This commit is contained in:
Hamcha 2016-02-20 21:40:24 +01:00
parent b1a4d4a8c7
commit c8ed6b77e9
7 changed files with 101 additions and 16 deletions

3
.gitignore vendored
View file

@ -4,4 +4,5 @@ clessy-broker
clessy-mods clessy-mods
clessy-stats clessy-stats
clessy-stats-import clessy-stats-import
stats-web/Chart.bundle.js stats-web/Chart.bundle.js
impact.ttf

View file

@ -1,13 +1,18 @@
package main package main
import ( import (
"net"
"github.com/hamcha/clessy/tg" "github.com/hamcha/clessy/tg"
) )
func executeClientCommand(action tg.ClientCommand) { func executeClientCommand(action tg.ClientCommand, client net.Conn) {
switch action.Type { switch action.Type {
case tg.CmdSendTextMessage: case tg.CmdSendTextMessage:
data := *(action.TextMessageData) data := *(action.TextMessageData)
api.SendTextMessage(data) api.SendTextMessage(data)
case tg.CmdGetFile:
data := *(action.FileRequestData)
api.GetFile(data, client, *action.Callback)
} }
} }

View file

@ -45,7 +45,7 @@ func handleClient(c net.Conn) {
log.Printf("%s\n", string(bytes)) log.Printf("%s\n", string(bytes))
continue continue
} }
executeClientCommand(cmd) executeClientCommand(cmd, c)
} }
removeCon(c) removeCon(c)
} }

View file

@ -1,9 +1,13 @@
package main package main
import ( import (
"encoding/base64"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt"
"io/ioutil"
"log" "log"
"net"
"net/http" "net/http"
"net/url" "net/url"
"strconv" "strconv"
@ -61,13 +65,77 @@ func (t Telegram) SendTextMessage(data tg.ClientTextMessageData) {
checkerr("SendTextMessage", err) checkerr("SendTextMessage", 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 tg.APIFile
err = json.NewDecoder(resp.Body).Decode(&filespecs)
if checkerr("GetFile/json.Decode", err) {
fail("Server sent garbage (or error)")
return
}
path := "https://api.telegram.org/file/bot" + t.Token + "/" + *filespecs.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 != *filespecs.Size {
// ???
log.Printf("[GetFile] WARN ?? Downloaded file does not match provided filesize: %d != %d\n", rawlen, *filespecs.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 { func (t Telegram) apiURL(method string) string {
return APIEndpoint + "bot" + t.Token + "/" + method return APIEndpoint + "bot" + t.Token + "/" + method
} }
func checkerr(method string, err error) bool { func checkerr(method string, err error) bool {
if err != nil { if err != nil {
log.Printf("Received error with call to %s: %s\n", method, err.Error()) log.Printf("[%s] Error: %s\n", method, err.Error())
return true return true
} }
return false return false

View file

@ -15,6 +15,7 @@ func initmods() {
func dispatch(broker *tg.Broker, update tg.APIMessage) { func dispatch(broker *tg.Broker, update tg.APIMessage) {
metafora(broker, update) metafora(broker, update)
viaggi(broker, update) viaggi(broker, update)
memegen(broker, update)
} }
func isCommand(update tg.APIMessage, cmdname string) bool { func isCommand(update tg.APIMessage, cmdname string) bool {

View file

@ -66,7 +66,13 @@ func (b *Broker) SendPhoto(chat *APIChat, data []byte, caption *string, original
// This function is asynchronous as data will be delivered to the given callback. // This function is asynchronous as data will be delivered to the given callback.
func (b *Broker) GetFile(fileID string, fn BrokerCallback) int { func (b *Broker) GetFile(fileID string, fn BrokerCallback) int {
cid := b.RegisterCallback(fn) cid := b.RegisterCallback(fn)
// Make file request b.sendCmd(ClientCommand{
Type: CmdGetFile,
FileRequestData: &FileRequestData{
FileID: fileID,
},
Callback: &cid,
})
return cid return cid
} }

View file

@ -9,14 +9,18 @@ const (
// BFile is a file retrieval response update // BFile is a file retrieval response update
BFile BrokerUpdateType = "file" 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 // BrokerUpdate is what is sent by the broker as update
type BrokerUpdate struct { type BrokerUpdate struct {
Type BrokerUpdateType Type BrokerUpdateType
Callback *int Callback *int `json:",omitempty"`
Message *APIMessage Error *string `json:",omitempty"`
Bytes *string Message *APIMessage `json:",omitempty"`
Bytes *string `json:",omitempty"`
} }
// ClientCommandType distinguishes requests sent by clients to the broker // ClientCommandType distinguishes requests sent by clients to the broker
@ -37,27 +41,27 @@ const (
type ClientTextMessageData struct { type ClientTextMessageData struct {
ChatID int ChatID int
Text string Text string
ReplyID *int ReplyID *int `json:",omitempty"`
} }
// ClientPhotoData is the required data for a CmdSendPhoto request // ClientPhotoData is the required data for a CmdSendPhoto request
type ClientPhotoData struct { type ClientPhotoData struct {
ChatID int ChatID int
Bytes string Bytes string
Caption *string Caption *string `json:",omitempty"`
ReplyID *int ReplyID *int `json:",omitempty"`
} }
// FileRequestData is the required data for a CmdGetFile request // FileRequestData is the required data for a CmdGetFile request
type FileRequestData struct { type FileRequestData struct {
FileID int FileID string
} }
// ClientCommand is a request sent by clients to the broker // ClientCommand is a request sent by clients to the broker
type ClientCommand struct { type ClientCommand struct {
Type ClientCommandType Type ClientCommandType
TextMessageData *ClientTextMessageData TextMessageData *ClientTextMessageData `json:",omitempty"`
PhotoData *ClientPhotoData PhotoData *ClientPhotoData `json:",omitempty"`
FileRequestData *FileRequestData FileRequestData *FileRequestData `json:",omitempty"`
Callback *int Callback *int `json:",omitempty"`
} }