+ Long polling to get events
+ ParseCommand sugar
This commit is contained in:
parent
98399bacff
commit
11849c8c18
11 changed files with 377 additions and 173 deletions
82
README.md
82
README.md
|
@ -13,8 +13,9 @@ Methods:
|
||||||
|
|
||||||
* SendMessage
|
* SendMessage
|
||||||
* UploadFile
|
* UploadFile
|
||||||
|
* FetchEvents
|
||||||
|
|
||||||
Webhooks to get updates
|
Webhooks workds but not recommends
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
|
||||||
|
@ -25,67 +26,60 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"time"
|
|
||||||
|
|
||||||
"gopkg.in/icq.v1"
|
"gopkg.in/icq.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// New API object
|
// New API object
|
||||||
b := icq.NewAPI(os.Getenv("ICQ_TOKEN"))
|
b := icq.NewAPI(os.Getenv("ICQ_TOKEN"))
|
||||||
|
|
||||||
// Send message
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
r, err := b.SendMessage(icq.Message{To: "429950", Text: "Hello, world!"})
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalln(err)
|
|
||||||
}
|
|
||||||
log.Println(r.State)
|
|
||||||
|
|
||||||
// Send file
|
ch := make(chan interface{}) // Events channel
|
||||||
f, err := os.Open("./example/icq.png")
|
|
||||||
defer f.Close()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalln(err)
|
|
||||||
}
|
|
||||||
file, err := b.UploadFile("icq.png", f)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalln(err)
|
|
||||||
}
|
|
||||||
b.SendMessage(icq.Message{To: "429950", Text: file.StaticUrl})
|
|
||||||
|
|
||||||
// Webhook usage
|
|
||||||
updates := make(chan icq.Update)
|
|
||||||
errors := make(chan error)
|
|
||||||
osSignal := make(chan os.Signal, 1)
|
osSignal := make(chan os.Signal, 1)
|
||||||
|
|
||||||
m := http.NewServeMux()
|
|
||||||
m.HandleFunc("/webhook", b.GetWebhookHandler(updates, errors)) // Webhook sets here
|
|
||||||
|
|
||||||
h := &http.Server{Addr: ":8080", Handler: m}
|
|
||||||
go func() {
|
|
||||||
log.Fatalln(h.ListenAndServe())
|
|
||||||
}()
|
|
||||||
signal.Notify(osSignal, os.Interrupt)
|
signal.Notify(osSignal, os.Interrupt)
|
||||||
signal.Notify(osSignal, os.Kill)
|
signal.Notify(osSignal, os.Kill)
|
||||||
|
|
||||||
|
go b.FetchEvents(ctx, ch) // Events fetch loop
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case u := <-updates:
|
case e := <-ch:
|
||||||
log.Println("Incomming message", u)
|
handleEvent(b, e)
|
||||||
|
case <-osSignal:
|
||||||
|
cancel()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleEvent(b *icq.API, event interface{}) {
|
||||||
|
switch event.(type) {
|
||||||
|
case *icq.IMEvent:
|
||||||
|
message := event.(*icq.IMEvent)
|
||||||
|
if err := handleMessage(b, message); err != nil {
|
||||||
b.SendMessage(icq.Message{
|
b.SendMessage(icq.Message{
|
||||||
To: u.Update.Chat.ID,
|
To: message.Data.Source.AimID,
|
||||||
Text: fmt.Sprintf("You sent me: %s", u.Update.Text),
|
Text: "Message process fail",
|
||||||
})
|
})
|
||||||
// ... process ICQ updates ...
|
}
|
||||||
case err := <-errors:
|
default:
|
||||||
log.Fatalln(err)
|
log.Printf("%#v", event)
|
||||||
case sig := <-osSignal:
|
|
||||||
ctx, _ := context.WithTimeout(context.Background(), 5*time.Second)
|
|
||||||
h.Shutdown(ctx)
|
|
||||||
log.Fatalln("OS signal:", sig.String())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handleMessage(b *icq.API, message *icq.IMEvent) error {
|
||||||
|
cmd, ok := icq.ParseCommand(message)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
_, err := b.SendMessage(icq.Message{
|
||||||
|
To: cmd.From,
|
||||||
|
Text: fmt.Sprintf("Command: %s, Arguments: %v", cmd.Command, cmd.Arguments),
|
||||||
|
})
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
79
api.go
79
api.go
|
@ -1,15 +1,11 @@
|
||||||
package icq
|
package icq
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// HTTP Client interface
|
// HTTP Client interface
|
||||||
|
@ -22,6 +18,7 @@ type API struct {
|
||||||
token string
|
token string
|
||||||
baseUrl string
|
baseUrl string
|
||||||
client Doer
|
client Doer
|
||||||
|
fetchBase string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAPI constructor of API object
|
// NewAPI constructor of API object
|
||||||
|
@ -33,80 +30,6 @@ func NewAPI(token string) *API {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendMessage with `message` text to `to` participant
|
|
||||||
func (a *API) SendMessage(message Message) (*MessageResponse, error) {
|
|
||||||
parse, _ := json.Marshal(message.Parse)
|
|
||||||
v := url.Values{}
|
|
||||||
v.Set("aimsid", a.token)
|
|
||||||
v.Set("r", strconv.FormatInt(time.Now().Unix(), 10))
|
|
||||||
v.Set("t", message.To)
|
|
||||||
v.Set("message", message.Text)
|
|
||||||
v.Set("mentions", strings.Join(message.Mentions, ","))
|
|
||||||
if len(message.Parse) > 0 {
|
|
||||||
v.Set("parse", string(parse))
|
|
||||||
}
|
|
||||||
b, err := a.send("/im/sendIM", v)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
r := &Response{}
|
|
||||||
if err := json.Unmarshal(b, r); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if r.Response.StatusCode != 200 {
|
|
||||||
return nil, fmt.Errorf("failed to send message: %s", r.Response.StatusText)
|
|
||||||
}
|
|
||||||
return r.Response.Data, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UploadFile to ICQ servers and returns URL to file
|
|
||||||
func (a *API) UploadFile(fileName string, r io.Reader) (*FileResponse, error) {
|
|
||||||
v := url.Values{}
|
|
||||||
v.Set("aimsid", a.token)
|
|
||||||
v.Set("filename", fileName)
|
|
||||||
req, err := http.NewRequest(http.MethodPost, a.baseUrl+"/im/sendFile?"+v.Encode(), r)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
resp, err := a.client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
b, err := ioutil.ReadAll(resp.Body)
|
|
||||||
file := struct {
|
|
||||||
Data FileResponse `json:"data"`
|
|
||||||
}{}
|
|
||||||
if err := json.Unmarshal(b, &file); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &file.Data, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetWebhookHandler returns http.HandleFunc that parses webhooks
|
|
||||||
func (a *API) GetWebhookHandler(cu chan<- Update, e chan<- error) func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
defer r.Body.Close()
|
|
||||||
if r.Method != http.MethodPost {
|
|
||||||
e <- fmt.Errorf("incorrect method: %s", r.Method)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
wr := &WebhookRequest{}
|
|
||||||
b, err := ioutil.ReadAll(r.Body)
|
|
||||||
if err != nil {
|
|
||||||
e <- err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err := json.Unmarshal(b, wr); err != nil {
|
|
||||||
e <- err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, u := range wr.Updates {
|
|
||||||
cu <- u
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *API) send(path string, v url.Values) ([]byte, error) {
|
func (a *API) send(path string, v url.Values) ([]byte, error) {
|
||||||
req, err := http.NewRequest(http.MethodPost, a.baseUrl+path, strings.NewReader(v.Encode()))
|
req, err := http.NewRequest(http.MethodPost, a.baseUrl+path, strings.NewReader(v.Encode()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
60
events.go
Normal file
60
events.go
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
package icq
|
||||||
|
|
||||||
|
type CommonEvent struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
SeqNum int `json:"seqNum"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServiceEvent struct {
|
||||||
|
CommonEvent
|
||||||
|
Data interface{} `json:"eventData"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type BuddyListEvent struct {
|
||||||
|
CommonEvent
|
||||||
|
Data struct {
|
||||||
|
Groups []struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
ID int `json:"id"`
|
||||||
|
Buddies []Buddy `json:"buddies"`
|
||||||
|
} `json:"groups"`
|
||||||
|
} `json:"eventData"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MyInfoEvent struct {
|
||||||
|
CommonEvent
|
||||||
|
Data Buddy `json:"eventData"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TypingStatus string
|
||||||
|
|
||||||
|
const (
|
||||||
|
StartTyping TypingStatus = "typing"
|
||||||
|
StopTyping = "none"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TypingEvent struct {
|
||||||
|
CommonEvent
|
||||||
|
Data struct {
|
||||||
|
AimID string `json:"aimId"`
|
||||||
|
TypingStatus TypingStatus `json:"typingStatus"`
|
||||||
|
} `json:"eventData"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type IMEvent struct {
|
||||||
|
CommonEvent
|
||||||
|
Data struct {
|
||||||
|
Autoresponse int `json:"autoresponse"`
|
||||||
|
Timestamp int `json:"timestamp"`
|
||||||
|
Notification string `json:"notification"`
|
||||||
|
MsgID string `json:"msgId"`
|
||||||
|
IMF string `json:"imf"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
RawMessage struct {
|
||||||
|
IPCountry string `json:"ipCountry"`
|
||||||
|
ClientCountry string `json:"clientCountry"`
|
||||||
|
Base64Msg string `json:"base64Msg"`
|
||||||
|
} `json:"rawMsg"`
|
||||||
|
Source Buddy `json:"source"`
|
||||||
|
} `json:"eventData"`
|
||||||
|
}
|
|
@ -3,67 +3,59 @@ package main
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/go-icq/icq"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/go-icq/icq"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// New API object
|
// New API object
|
||||||
b := icq.NewAPI(os.Getenv("ICQ_TOKEN"))
|
b := icq.NewAPI(os.Getenv("ICQ_TOKEN"))
|
||||||
|
|
||||||
// Send message
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
r, err := b.SendMessage(icq.Message{To: "429950", Text: "Hello, world!"})
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalln(err)
|
|
||||||
}
|
|
||||||
log.Println(r.State)
|
|
||||||
|
|
||||||
// Send file
|
ch := make(chan interface{}) // Events channel
|
||||||
f, err := os.Open("./example/icq.png")
|
|
||||||
defer f.Close()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalln(err)
|
|
||||||
}
|
|
||||||
file, err := b.UploadFile("icq.png", f)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalln(err)
|
|
||||||
}
|
|
||||||
b.SendMessage(icq.Message{To: "429950", Text: file.StaticUrl})
|
|
||||||
|
|
||||||
// Webhook usage
|
|
||||||
updates := make(chan icq.Update)
|
|
||||||
errors := make(chan error)
|
|
||||||
osSignal := make(chan os.Signal, 1)
|
osSignal := make(chan os.Signal, 1)
|
||||||
|
|
||||||
m := http.NewServeMux()
|
|
||||||
m.HandleFunc("/webhook", b.GetWebhookHandler(updates, errors)) // Webhook sets here
|
|
||||||
|
|
||||||
h := &http.Server{Addr: ":8080", Handler: m}
|
|
||||||
go func() {
|
|
||||||
log.Fatalln(h.ListenAndServe())
|
|
||||||
}()
|
|
||||||
signal.Notify(osSignal, os.Interrupt)
|
signal.Notify(osSignal, os.Interrupt)
|
||||||
signal.Notify(osSignal, os.Kill)
|
signal.Notify(osSignal, os.Kill)
|
||||||
|
|
||||||
|
go b.FetchEvents(ctx, ch) // Events fetch loop
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case u := <-updates:
|
case e := <-ch:
|
||||||
log.Printf("Incomming message %#v", u)
|
handleEvent(b, e)
|
||||||
|
case <-osSignal:
|
||||||
|
cancel()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleEvent(b *icq.API, event interface{}) {
|
||||||
|
switch event.(type) {
|
||||||
|
case *icq.IMEvent:
|
||||||
|
message := event.(*icq.IMEvent)
|
||||||
|
if err := handleMessage(b, message); err != nil {
|
||||||
b.SendMessage(icq.Message{
|
b.SendMessage(icq.Message{
|
||||||
To: u.Update.Chat.ID,
|
To: message.Data.Source.AimID,
|
||||||
Text: fmt.Sprintf("You sent me: %s", u.Update.Text),
|
Text: "Message process fail",
|
||||||
})
|
})
|
||||||
// ... process ICQ updates ...
|
}
|
||||||
case err := <-errors:
|
default:
|
||||||
log.Fatalln(err)
|
log.Printf("%#v", event)
|
||||||
case sig := <-osSignal:
|
|
||||||
ctx, _ := context.WithTimeout(context.Background(), 5*time.Second)
|
|
||||||
h.Shutdown(ctx)
|
|
||||||
log.Fatalln("OS signal:", sig.String())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handleMessage(b *icq.API, message *icq.IMEvent) error {
|
||||||
|
cmd, ok := icq.ParseCommand(message)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
_, err := b.SendMessage(icq.Message{
|
||||||
|
To: cmd.From,
|
||||||
|
Text: fmt.Sprintf("Command: %s, Arguments: %v", cmd.Command, cmd.Arguments),
|
||||||
|
})
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
BIN
example/icq.png
BIN
example/icq.png
Binary file not shown.
Before Width: | Height: | Size: 619 KiB |
89
fetchEvents.go
Normal file
89
fetchEvents.go
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
package icq
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (a *API) FetchEvents(ctx context.Context, ch chan interface{}) error {
|
||||||
|
fetchResp := &struct {
|
||||||
|
Response struct {
|
||||||
|
Data struct {
|
||||||
|
FetchBase string `json:"fetchBaseURL"`
|
||||||
|
PollTime int `json:"pollTime"`
|
||||||
|
Events []json.RawMessage `json:"events"`
|
||||||
|
} `json:"data"`
|
||||||
|
} `json:"response"`
|
||||||
|
}{}
|
||||||
|
for {
|
||||||
|
b := []byte{}
|
||||||
|
u := a.fetchBase
|
||||||
|
if u == "" {
|
||||||
|
v := url.Values{}
|
||||||
|
v.Set("aimsid", a.token)
|
||||||
|
v.Set("first", "1")
|
||||||
|
u = a.baseUrl + "/fetchEvents?" + v.Encode()
|
||||||
|
}
|
||||||
|
req, err := http.Get(u)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
b, err = ioutil.ReadAll(req.Body)
|
||||||
|
req.Body.Close()
|
||||||
|
if err := json.Unmarshal(b, fetchResp); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
a.fetchBase = fetchResp.Response.Data.FetchBase
|
||||||
|
for _, e := range fetchResp.Response.Data.Events {
|
||||||
|
ce := &CommonEvent{}
|
||||||
|
if err := json.Unmarshal(e, ce); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch ce.Type {
|
||||||
|
case "service":
|
||||||
|
ev := &ServiceEvent{}
|
||||||
|
if err := json.Unmarshal(e, ev); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ch <- ev
|
||||||
|
case "buddylist":
|
||||||
|
ev := &BuddyListEvent{}
|
||||||
|
if err := json.Unmarshal(e, ev); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ch <- ev
|
||||||
|
case "myInfo":
|
||||||
|
ev := &MyInfoEvent{}
|
||||||
|
if err := json.Unmarshal(e, ev); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ch <- ev
|
||||||
|
case "typing":
|
||||||
|
ev := &TypingEvent{}
|
||||||
|
if err := json.Unmarshal(e, ev); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ch <- ev
|
||||||
|
case "im":
|
||||||
|
ev := &IMEvent{}
|
||||||
|
if err := json.Unmarshal(e, ev); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ch <- ev
|
||||||
|
default:
|
||||||
|
log.Printf("Unknown event %s: %#v", ce.Type, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-time.After(time.Duration(fetchResp.Response.Data.PollTime)):
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
36
sendMessage.go
Normal file
36
sendMessage.go
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
package icq
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SendMessage with `message` text to `to` participant
|
||||||
|
func (a *API) SendMessage(message Message) (*MessageResponse, error) {
|
||||||
|
parse, _ := json.Marshal(message.Parse)
|
||||||
|
v := url.Values{}
|
||||||
|
v.Set("aimsid", a.token)
|
||||||
|
v.Set("r", strconv.FormatInt(time.Now().Unix(), 10))
|
||||||
|
v.Set("t", message.To)
|
||||||
|
v.Set("message", message.Text)
|
||||||
|
v.Set("mentions", strings.Join(message.Mentions, ","))
|
||||||
|
if len(message.Parse) > 0 {
|
||||||
|
v.Set("parse", string(parse))
|
||||||
|
}
|
||||||
|
b, err := a.send("/im/sendIM", v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
r := &Response{}
|
||||||
|
if err := json.Unmarshal(b, r); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if r.Response.StatusCode != 200 {
|
||||||
|
return nil, fmt.Errorf("failed to send message: %s", r.Response.StatusText)
|
||||||
|
}
|
||||||
|
return r.Response.Data, nil
|
||||||
|
}
|
13
types.go
13
types.go
|
@ -8,6 +8,7 @@ type Response struct {
|
||||||
Data *MessageResponse `json:"data"`
|
Data *MessageResponse `json:"data"`
|
||||||
} `json:"response"`
|
} `json:"response"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ParseType string
|
type ParseType string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -66,3 +67,15 @@ type User struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
LanguageCode string `json:"language_code"`
|
LanguageCode string `json:"language_code"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Buddy struct {
|
||||||
|
AimID string `json:"aimId"`
|
||||||
|
DisplayID string `json:"displayId"`
|
||||||
|
FriendlyName string `json:"friendly"`
|
||||||
|
State string `json:"state"`
|
||||||
|
UserType string `json:"userType"`
|
||||||
|
UserAgreement []string `json:"userAgreement"`
|
||||||
|
Nick string `json:"nick"`
|
||||||
|
GlobalFlags int `json:"globalFlags"`
|
||||||
|
BuddyIcon string `json:"buddyIcon"`
|
||||||
|
}
|
||||||
|
|
33
uploadFile.go
Normal file
33
uploadFile.go
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
package icq
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UploadFile to ICQ servers and returns URL to file
|
||||||
|
func (a *API) UploadFile(fileName string, r io.Reader) (*FileResponse, error) {
|
||||||
|
v := url.Values{}
|
||||||
|
v.Set("aimsid", a.token)
|
||||||
|
v.Set("filename", fileName)
|
||||||
|
req, err := http.NewRequest(http.MethodPost, a.baseUrl+"/im/sendFile?"+v.Encode(), r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
resp, err := a.client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
b, err := ioutil.ReadAll(resp.Body)
|
||||||
|
file := struct {
|
||||||
|
Data FileResponse `json:"data"`
|
||||||
|
}{}
|
||||||
|
if err := json.Unmarshal(b, &file); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &file.Data, nil
|
||||||
|
}
|
31
utils.go
Normal file
31
utils.go
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
package icq
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Command is sugar on top of IMEvent that represented standard ICQ bot commands
|
||||||
|
type Command struct {
|
||||||
|
From string
|
||||||
|
Command string
|
||||||
|
Arguments []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseCommand from IMEvent
|
||||||
|
// Command must starts from '.' or '/'. Arguments separated by space (' ')
|
||||||
|
func ParseCommand(event *IMEvent) (*Command, bool) {
|
||||||
|
message := event.Data.Message
|
||||||
|
parts := strings.Split(message, " ")
|
||||||
|
if len(parts) == 0 {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
if parts[0][0] != '.' && parts[0][0] != '/' {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Command{
|
||||||
|
From: event.Data.Source.AimID,
|
||||||
|
Command: string(parts[0][1:]),
|
||||||
|
Arguments: parts[1:],
|
||||||
|
}, true
|
||||||
|
}
|
33
webhookHandler.go
Normal file
33
webhookHandler.go
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
package icq
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetWebhookHandler returns http.HandleFunc that parses webhooks
|
||||||
|
// Warning! Not fully functional at ICQ now!
|
||||||
|
func (a *API) GetWebhookHandler(cu chan<- Update, e chan<- error) func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
defer r.Body.Close()
|
||||||
|
if r.Method != http.MethodPost {
|
||||||
|
e <- fmt.Errorf("incorrect method: %s", r.Method)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
wr := &WebhookRequest{}
|
||||||
|
b, err := ioutil.ReadAll(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
e <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(b, wr); err != nil {
|
||||||
|
e <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, u := range wr.Updates {
|
||||||
|
cu <- u
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue