mirror of
				https://github.com/42wim/matterbridge.git
				synced 2025-10-31 13:57:25 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			257 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			257 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package gozulipbot
 | |
| 
 | |
| import (
 | |
| 	"encoding/json"
 | |
| 	"fmt"
 | |
| 	"io/ioutil"
 | |
| 	"net/http"
 | |
| 	"strings"
 | |
| 	"time"
 | |
| )
 | |
| 
 | |
| type Bot struct {
 | |
| 	APIKey  string
 | |
| 	APIURL  string
 | |
| 	Email   string
 | |
| 	Queues  []*Queue
 | |
| 	Streams []string
 | |
| 	Client  Doer
 | |
| 	Backoff time.Duration
 | |
| 	Retries int64
 | |
| }
 | |
| 
 | |
| type Doer interface {
 | |
| 	Do(*http.Request) (*http.Response, error)
 | |
| }
 | |
| 
 | |
| // Init adds an http client to an existing bot struct.
 | |
| func (b *Bot) Init() *Bot {
 | |
| 	b.Client = &http.Client{}
 | |
| 	return b
 | |
| }
 | |
| 
 | |
| // GetStreamList gets the raw http response when requesting all public streams.
 | |
| func (b *Bot) GetStreamList() (*http.Response, error) {
 | |
| 	req, err := b.constructRequest("GET", "streams", "")
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	return b.Client.Do(req)
 | |
| }
 | |
| 
 | |
| type StreamJSON struct {
 | |
| 	Msg     string `json:"msg"`
 | |
| 	Streams []struct {
 | |
| 		StreamID    int    `json:"stream_id"`
 | |
| 		InviteOnly  bool   `json:"invite_only"`
 | |
| 		Description string `json:"description"`
 | |
| 		Name        string `json:"name"`
 | |
| 	} `json:"streams"`
 | |
| 	Result string `json:"result"`
 | |
| }
 | |
| 
 | |
| // GetStreams returns a list of all public streams
 | |
| func (b *Bot) GetStreams() ([]string, error) {
 | |
| 	resp, err := b.GetStreamList()
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	defer resp.Body.Close()
 | |
| 	body, err := ioutil.ReadAll(resp.Body)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	var sj StreamJSON
 | |
| 	err = json.Unmarshal(body, &sj)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	var streams []string
 | |
| 	for _, s := range sj.Streams {
 | |
| 		streams = append(streams, s.Name)
 | |
| 	}
 | |
| 
 | |
| 	return streams, nil
 | |
| }
 | |
| 
 | |
| // GetStreams returns a list of all public streams
 | |
| func (b *Bot) GetRawStreams() (StreamJSON, error) {
 | |
| 	var sj StreamJSON
 | |
| 	resp, err := b.GetStreamList()
 | |
| 	if err != nil {
 | |
| 		return sj, err
 | |
| 	}
 | |
| 	defer resp.Body.Close()
 | |
| 	body, err := ioutil.ReadAll(resp.Body)
 | |
| 	if err != nil {
 | |
| 		return sj, err
 | |
| 	}
 | |
| 
 | |
| 	err = json.Unmarshal(body, &sj)
 | |
| 	if err != nil {
 | |
| 		return sj, err
 | |
| 	}
 | |
| 	return sj, nil
 | |
| }
 | |
| 
 | |
| // Subscribe will set the bot to receive messages from the given streams.
 | |
| // If no streams are given, it will subscribe the bot to the streams in the bot struct.
 | |
| func (b *Bot) Subscribe(streams []string) (*http.Response, error) {
 | |
| 	if streams == nil {
 | |
| 		streams = b.Streams
 | |
| 	}
 | |
| 
 | |
| 	var toSubStreams []map[string]string
 | |
| 	for _, name := range streams {
 | |
| 		toSubStreams = append(toSubStreams, map[string]string{"name": name})
 | |
| 	}
 | |
| 
 | |
| 	bodyBts, err := json.Marshal(toSubStreams)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	body := "subscriptions=" + string(bodyBts)
 | |
| 
 | |
| 	req, err := b.constructRequest("POST", "users/me/subscriptions", body)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	return b.Client.Do(req)
 | |
| }
 | |
| 
 | |
| // Unsubscribe will remove the bot from the given streams.
 | |
| // If no streams are given, nothing will happen and the function will error.
 | |
| func (b *Bot) Unsubscribe(streams []string) (*http.Response, error) {
 | |
| 	if len(streams) == 0 {
 | |
| 		return nil, fmt.Errorf("No streams were provided")
 | |
| 	}
 | |
| 
 | |
| 	body := `delete=["` + strings.Join(streams, `","`) + `"]`
 | |
| 
 | |
| 	req, err := b.constructRequest("PATCH", "users/me/subscriptions", body)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	return b.Client.Do(req)
 | |
| }
 | |
| 
 | |
| func (b *Bot) ListSubscriptions() (*http.Response, error) {
 | |
| 	req, err := b.constructRequest("GET", "users/me/subscriptions", "")
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	return b.Client.Do(req)
 | |
| }
 | |
| 
 | |
| type EventType string
 | |
| 
 | |
| const (
 | |
| 	Messages      EventType = "messages"
 | |
| 	Subscriptions EventType = "subscriptions"
 | |
| 	RealmUser     EventType = "realm_user"
 | |
| 	Pointer       EventType = "pointer"
 | |
| )
 | |
| 
 | |
| type Narrow string
 | |
| 
 | |
| const (
 | |
| 	NarrowPrivate Narrow = `[["is", "private"]]`
 | |
| 	NarrowAt      Narrow = `[["is", "mentioned"]]`
 | |
| )
 | |
| 
 | |
| // RegisterEvents adds a queue to the bot. It includes the EventTypes and
 | |
| // Narrow given. If neither is given, it will default to all Messages.
 | |
| func (b *Bot) RegisterEvents(ets []EventType, n Narrow) (*Queue, error) {
 | |
| 	resp, err := b.RawRegisterEvents(ets, n)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	defer resp.Body.Close()
 | |
| 
 | |
| 	body, err := ioutil.ReadAll(resp.Body)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	q := &Queue{Bot: b}
 | |
| 	err = json.Unmarshal(body, q)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	if q.LastEventID < q.MaxMessageID {
 | |
| 		q.LastEventID = q.MaxMessageID
 | |
| 	}
 | |
| 
 | |
| 	b.Queues = append(b.Queues, q)
 | |
| 
 | |
| 	return q, nil
 | |
| }
 | |
| 
 | |
| func (b *Bot) RegisterAll() (*Queue, error) {
 | |
| 	return b.RegisterEvents(nil, "")
 | |
| }
 | |
| 
 | |
| func (b *Bot) RegisterAt() (*Queue, error) {
 | |
| 	return b.RegisterEvents(nil, NarrowAt)
 | |
| }
 | |
| 
 | |
| func (b *Bot) RegisterPrivate() (*Queue, error) {
 | |
| 	return b.RegisterEvents(nil, NarrowPrivate)
 | |
| }
 | |
| 
 | |
| func (b *Bot) RegisterSubscriptions() (*Queue, error) {
 | |
| 	events := []EventType{Subscriptions}
 | |
| 	return b.RegisterEvents(events, "")
 | |
| }
 | |
| 
 | |
| // RawRegisterEvents tells Zulip to include message events in the bots events queue.
 | |
| // Passing nil as the slice of EventType will default to receiving Messages
 | |
| func (b *Bot) RawRegisterEvents(ets []EventType, n Narrow) (*http.Response, error) {
 | |
| 	// default to Messages if no EventTypes given
 | |
| 	query := `event_types=["message"]`
 | |
| 
 | |
| 	if len(ets) != 0 {
 | |
| 		query = `event_types=["`
 | |
| 		for i, s := range ets {
 | |
| 			query += fmt.Sprintf("%s", s)
 | |
| 			if i != len(ets)-1 {
 | |
| 				query += `", "`
 | |
| 			}
 | |
| 		}
 | |
| 		query += `"]`
 | |
| 	}
 | |
| 
 | |
| 	if n != "" {
 | |
| 		query += fmt.Sprintf("&narrow=%s", n)
 | |
| 	}
 | |
| 	query += fmt.Sprintf("&all_public_streams=true")
 | |
| 	req, err := b.constructRequest("POST", "register", query)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	return b.Client.Do(req)
 | |
| }
 | |
| 
 | |
| // constructRequest makes a zulip request and ensures the proper headers are set.
 | |
| func (b *Bot) constructRequest(method, endpoint, body string) (*http.Request, error) {
 | |
| 	url := b.APIURL + endpoint
 | |
| 	req, err := http.NewRequest(method, url, strings.NewReader(body))
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
 | |
| 	req.SetBasicAuth(b.Email, b.APIKey)
 | |
| 
 | |
| 	return req, nil
 | |
| }
 | 
