This repository has been archived by the owner on Aug 29, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
19 changed files
with
953 additions
and
504 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,85 +1,72 @@ | ||
# ICQ Bot API | ||
# ICQ Bot Api Go | ||
|
||
## Installation | ||
[![Sourcegraph](https://sourcegraph.com/github.com/go-icq/icq/-/badge.svg?style=flat-square)](https://sourcegraph.com/github.com/go-icq/icq?badge) | ||
[![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](http://godoc.org/github.com/go-icq/icq) | ||
[![Go Report Card](https://goreportcard.com/badge/github.com/go-icq/icq?style=flat-square)](https://goreportcard.com/report/github.com/go-icq/icq) | ||
[![License](http://img.shields.io/badge/license-mit-blue.svg?style=flat-square)](https://raw.githubusercontent.com/go-icq/icq/master/LICENSE) | ||
|
||
Go get: `go get gopkg.in/icq.v2` | ||
Основана на новом Bot Api (https://icq.com/botapi/) | ||
|
||
Go mod / Go dep: `import "gopkg.in/icq.v2"` | ||
Реализованы все методы и соответствуют документации. | ||
|
||
|
||
## Working | ||
|
||
Methods: | ||
|
||
* SendMessage | ||
* UploadFile | ||
* FetchEvents | ||
|
||
Webhooks workds but not recommends | ||
|
||
## Example | ||
## Пример | ||
|
||
```go | ||
package main | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"log" | ||
"os" | ||
"os/signal" | ||
"time" | ||
|
||
"gopkg.in/icq.v2" | ||
"github.com/go-icq/icq" | ||
) | ||
|
||
func main() { | ||
// New API object | ||
b := icq.NewAPI(os.Getenv("ICQ_TOKEN")) | ||
// Инициализация | ||
b := icq.NewApi(os.Getenv("ICQ_TOKEN"), icq.ICQ) // or icq.Agent | ||
|
||
ctx, cancel := context.WithCancel(context.Background()) | ||
// Получение информации о боте | ||
log.Println(b.Self.Get()) | ||
|
||
ch := make(chan interface{}) // Events channel | ||
osSignal := make(chan os.Signal, 1) | ||
signal.Notify(osSignal, os.Interrupt) | ||
signal.Notify(osSignal, os.Kill) | ||
// Отправка сообщения | ||
resultSend, err := b.Messages.SendText("429950", "Привет!", nil, "", "") | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
go b.FetchEvents(ctx, ch) // Events fetch loop | ||
// Отправка файла | ||
resultFile, err := b.Messages.SendFile("429950", "./example/example.jpg", "коржик", []string{resultSend.MsgID}, "", "") | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
for { | ||
select { | ||
case e := <-ch: | ||
handleEvent(b, e) | ||
case <-osSignal: | ||
cancel() | ||
break | ||
} | ||
// Отправка существующего файла по ID | ||
_, err = b.Messages.SendExistsFile("429950", resultFile.FileID, "Существующий файл", nil, "", "") | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
} | ||
|
||
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{ | ||
To: message.Data.Source.AimID, | ||
Text: "Message process fail", | ||
}) | ||
} | ||
default: | ||
log.Printf("%#v", event) | ||
// Редактирование сообщения | ||
_, err = b.Messages.EditText("429950", "Новый текст", resultSend.MsgID) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
} | ||
|
||
func handleMessage(b *icq.API, message *icq.IMEvent) error { | ||
cmd, ok := icq.ParseCommand(message) | ||
if !ok { | ||
return nil | ||
// Будем слушать эвенты 5 минут. При закрытии контекста перестает работать цикл получения событий. В реальном мире контекст надо будет закрывать по сигналу ОС | ||
ctx, _ := context.WithTimeout(context.Background(), 5*time.Minute) | ||
for ev := range b.Events.Get(ctx) { | ||
switch ev := ev.(type) { | ||
case *icq.EventDataMessage: | ||
b.Messages.SendText(ev.Payload.Chat.ChatID, "Echo: "+ev.Payload.Text, []string{ev.Payload.MsgID}, "", "") | ||
default: | ||
log.Println(ev) | ||
} | ||
} | ||
_, err := b.SendMessage(icq.Message{ | ||
To: cmd.From, | ||
Text: fmt.Sprintf("Command: %s, Arguments: %v", cmd.Command, cmd.Arguments), | ||
}) | ||
return err | ||
} | ||
``` | ||
|
||
## Автор | ||
|
||
Александр NeonXP Кирюхин <[email protected]> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
package icq | ||
|
||
import ( | ||
"encoding/json" | ||
"net/http" | ||
"net/url" | ||
) | ||
|
||
type chats struct { | ||
client *client | ||
} | ||
|
||
func newChats(client *client) *chats { | ||
return &chats{client: client} | ||
} | ||
|
||
func (s *chats) SendActions(chatID string, actions []ChatAction) (bool, error) { | ||
acts := []string{} | ||
for _, act := range actions { | ||
acts = append(acts, string(act)) | ||
} | ||
resp, err := s.client.request( | ||
http.MethodGet, | ||
"/chats/sendActions", | ||
url.Values{ | ||
"chatId": []string{chatID}, | ||
"actions": acts, | ||
}, | ||
nil, | ||
) | ||
if err != nil { | ||
return false, err | ||
} | ||
result := new(OK) | ||
return result.OK, json.NewDecoder(resp).Decode(result) | ||
} | ||
|
||
func (s *chats) GetInfo(chatID string) (*Chat, error) { | ||
resp, err := s.client.request( | ||
http.MethodGet, | ||
"/chats/getInfo", | ||
url.Values{ | ||
"chatId": []string{chatID}, | ||
}, | ||
nil, | ||
) | ||
if err != nil { | ||
return nil, err | ||
} | ||
result := new(Chat) | ||
return result, json.NewDecoder(resp).Decode(result) | ||
} | ||
|
||
func (s *chats) GetAdmins(chatID string) (*Admins, error) { | ||
resp, err := s.client.request( | ||
http.MethodGet, | ||
"/chats/getAdmins", | ||
url.Values{ | ||
"chatId": []string{chatID}, | ||
}, | ||
nil, | ||
) | ||
if err != nil { | ||
return nil, err | ||
} | ||
result := new(Admins) | ||
return result, json.NewDecoder(resp).Decode(result) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
package icq | ||
|
||
import ( | ||
"bytes" | ||
"encoding/json" | ||
"fmt" | ||
"io" | ||
"io/ioutil" | ||
"net/http" | ||
"net/url" | ||
"path" | ||
"time" | ||
) | ||
|
||
type ApiType int | ||
|
||
const ( | ||
ICQ ApiType = iota | ||
Agent | ||
) | ||
|
||
var servers = map[ApiType]string{ | ||
ICQ: "https://api.icq.net/bot/v1/", | ||
Agent: "https://agent.mail.ru/bot/v1/", | ||
} | ||
|
||
type client struct { | ||
token string | ||
apiType ApiType | ||
client http.Client | ||
} | ||
|
||
func newClient(token string, apiType ApiType) *client { | ||
return &client{token: token, apiType: apiType, client: http.Client{Timeout: 30 * time.Second}} | ||
} | ||
|
||
func (c *client) request(method string, methodPath string, query url.Values, body *bytes.Buffer) (io.Reader, error) { | ||
return c.requestWithContentType(method, methodPath, query, body, "") | ||
} | ||
|
||
func (c *client) requestWithContentType(method string, methodPath string, query url.Values, body *bytes.Buffer, contentType string) (io.Reader, error) { | ||
query.Set("token", c.token) | ||
u, err := url.Parse(servers[c.apiType]) | ||
if err != nil { | ||
return nil, err | ||
} | ||
u.Path = path.Join(u.Path, methodPath) | ||
u.RawQuery = query.Encode() | ||
|
||
req, err := http.NewRequest(method, u.String(), nil) | ||
if contentType != "" { | ||
req.Header.Set("Content-Type", contentType) | ||
} | ||
if body != nil { | ||
rc := ioutil.NopCloser(body) | ||
req.Body = rc | ||
} | ||
|
||
if err != nil { | ||
return nil, err | ||
} | ||
resp, err := c.client.Do(req) | ||
if err != nil { | ||
return nil, err | ||
} | ||
if resp.StatusCode != http.StatusOK { | ||
errObj := new(Error) | ||
err = json.NewDecoder(resp.Body).Decode(errObj) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return nil, fmt.Errorf("ok=%v message=%s", errObj.OK, errObj.Description) | ||
} | ||
return resp.Body, err | ||
} |
Oops, something went wrong.