Passed
Push — master ( 016bd9...7ba52e )
by Viktor
01:42
created

bot.*Twitch.AddStreamer   C

Complexity

Conditions 9

Size

Total Lines 34
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 27
nop 3
dl 0
loc 34
rs 6.6666
c 0
b 0
f 0
1
package bot
2
3
import (
4
	"encoding/json"
5
	"errors"
6
	"fmt"
7
	"github.com/bwmarrin/discordgo"
8
	"net/http"
9
	"strings"
10
	"time"
11
)
12
13
type Twitch struct {
0 ignored issues
show
introduced by
exported type Twitch should have comment or be unexported
Loading history...
14
	Guilds  map[string]*TwitchGuild
15
	DB      *DBWorker
16
	Conf    *Config
17
	Discord *discordgo.Session
18
}
19
20
type TwitchGuild struct {
0 ignored issues
show
introduced by
exported type TwitchGuild should have comment or be unexported
Loading history...
21
	ID      string
22
	Streams []*TwitchStream
23
}
24
25
type TwitchStream struct {
0 ignored issues
show
introduced by
exported type TwitchStream should have comment or be unexported
Loading history...
26
	Login          string
27
	Guild          string
28
	Channel        string
29
	IsOnline       bool
30
	IsCustom       bool
31
	CustomMessage  string
32
	CustomImageURI string
33
}
34
35
type TwitchStreamResult struct {
0 ignored issues
show
introduced by
exported type TwitchStreamResult should have comment or be unexported
Loading history...
36
	Data []TwitchStreamData `json:"data"`
37
}
38
39
type TwitchStreamData struct {
0 ignored issues
show
introduced by
exported type TwitchStreamData should have comment or be unexported
Loading history...
40
	ID           string `json:"id"`
41
	UserID       string `json:"user_id"`
42
	UserName     string `json:"user_name"`
43
	GameID       string `json:"game_id"`
44
	Type         string `json:"type"`
45
	Title        string `json:"title"`
46
	Viewers      int    `json:"viewer_count"`
47
	Language     string `json:"language"`
48
	ThumbnailURL string `json:"thumbnail_url"`
49
}
50
51
type TwitchUserResult struct {
0 ignored issues
show
introduced by
exported type TwitchUserResult should have comment or be unexported
Loading history...
52
	Data []TwitchUserData `json:"data"`
53
}
54
55
type TwitchUserData struct {
0 ignored issues
show
introduced by
exported type TwitchUserData should have comment or be unexported
Loading history...
56
	ID              string `json:"id"`
57
	Login           string `json:"login"`
58
	Name            string `json:"display_name"`
59
	Type            string `json:"type"`
60
	BroadcasterType string `json:"broadcaster_type"`
61
	Description     string `json:"description"`
62
	ProfileImgURL   string `json:"profile_image_url"`
63
	OfflineImgURL   string `json:"offline_image_url"`
64
	Views           int    `json:"view_count"`
65
}
66
67
type TwitchGameResult struct {
0 ignored issues
show
introduced by
exported type TwitchGameResult should have comment or be unexported
Loading history...
68
	Data []TwitchGameData `json:"data"`
69
}
70
71
type TwitchGameData struct {
0 ignored issues
show
introduced by
exported type TwitchGameData should have comment or be unexported
Loading history...
72
	ID     string `json:"id"`
73
	Name   string `json:"name"`
74
	ArtURL string `json:"box_art_url"`
75
}
76
77
// TwitchInit makes new instance of twitch api worker
78
func TwitchInit(session *discordgo.Session, conf *Config, db *DBWorker) *Twitch {
79
	guilds := make(map[string]*TwitchGuild)
80
	var streams []*TwitchStream
81
	for _, g := range session.State.Guilds {
82
		guildStreams := db.GetTwitchStreams(g.ID)
83
		for _, s := range guildStreams {
84
			streams = append(streams, s)
85
		}
86
		guilds[g.ID] = &TwitchGuild{g.ID, guildStreams}
87
	}
88
	fmt.Printf("Loaded [%v] streamers", len(streams))
89
	return &Twitch{guilds, db, conf, session}
90
}
91
92
// Update updates status of streamers and notify
93
func (t *Twitch) Update() {
94
	for _,g := range t.Guilds {
95
		for _, s := range g.Streams {
96
			timeout := time.Duration(time.Duration(1) * time.Second)
97
			client := &http.Client{
98
				Timeout: time.Duration(timeout),
99
			}
100
			req, _ := http.NewRequest("GET", fmt.Sprintf("https://api.twitch.tv/helix/streams?user_login=%v", s.Login), nil)
101
			req.Header.Add("Client-ID", t.Conf.Twitch.ClientID)
102
			resp, err := client.Do(req)
103
			var result TwitchStreamResult
104
			var gameResult TwitchGameResult
105
			if err == nil {
106
				err = json.NewDecoder(resp.Body).Decode(&result)
107
				if err != nil {
108
					t.DB.Log("Twitch", "", "Parsing Twitch API stream error")
109
					continue
110
				}
111
				if len(result.Data) > 0 {
112
					greq, _ := http.NewRequest("GET", fmt.Sprintf("https://api.twitch.tv/helix/games?id=%v", result.Data[0].GameID), nil)
113
					greq.Header.Add("Client-ID", t.Conf.Twitch.ClientID)
114
					gresp, gerr := client.Do(greq)
115
					err = json.NewDecoder(gresp.Body).Decode(&gameResult)
116
					if gerr != nil {
117
						t.DB.Log("Twitch", "", "Parsing Twitch API game error")
118
					}
119
					if s.IsOnline == false {
120
						s.IsOnline = true
121
						t.DB.UpdateStream(s)
122
						imgUrl := strings.Replace(result.Data[0].ThumbnailURL, "{width}", "720", -1)
0 ignored issues
show
introduced by
var imgUrl should be imgURL
Loading history...
123
						imgUrl = strings.Replace(imgUrl, "{height}", "480", -1)
124
						emb := NewEmbed(result.Data[0].UserName).
125
							Field("Title", result.Data[0].Title, false).
126
							Field("Viewers", fmt.Sprintf("%v", result.Data[0].Viewers), true).
127
							Field("Game", gameResult.Data[0].Name, true).
128
							AttachImgURL(imgUrl).
129
							Color(t.Conf.General.EmbedColor)
130
						_, _ = t.Discord.ChannelMessageSend(s.Channel, fmt.Sprintf(t.Conf.GetLocaleLang("twitch_online", result.Data[0].Language), result.Data[0].UserName, s.Login))
0 ignored issues
show
introduced by
can't check non-constant format in call to Sprintf
Loading history...
131
						_, _ = t.Discord.ChannelMessageSendEmbed(s.Channel, emb.GetEmbed())
132
					}
133
				} else {
134
					if s.IsOnline == true {
135
						s.IsOnline = false
136
						t.DB.UpdateStream(s)
137
					}
138
				}
139
140
			}
141
		}
142
	}
143
}
144
145
// AddStreamer adds new streamer to list
146
func (t *Twitch) AddStreamer(guild, channel, login string) (string, error) {
147
	if g, ok := t.Guilds[guild]; ok {
148
		for _, s := range g.Streams {
149
			if s.Guild == guild && s.Login == login {
150
				return "", errors.New("streamer already exists")
151
			}
152
		}
153
		timeout := time.Duration(time.Duration(1) * time.Second)
154
		client := &http.Client{
155
			Timeout: time.Duration(timeout),
156
		}
157
		req, _ := http.NewRequest("GET", fmt.Sprintf("https://api.twitch.tv/helix/users?login=%v", login), nil)
158
		req.Header.Add("Client-ID", t.Conf.Twitch.ClientID)
159
		resp, err := client.Do(req)
160
		var result TwitchUserResult
161
		if err == nil {
162
			err = json.NewDecoder(resp.Body).Decode(&result)
163
			if err != nil {
164
				return "", errors.New("parsing streamer error")
165
			}
166
			if len(result.Data) > 0 {
167
				stream := TwitchStream{}
168
				stream.Login = login
169
				stream.Channel = channel
170
				stream.Guild = guild
171
				t.Guilds[guild].Streams = append(t.Guilds[guild].Streams, &stream)
172
				t.DB.AddStream(&stream)
173
			}
174
		} else {
175
			return "", errors.New("getting streamer error")
176
		}
177
		return result.Data[0].Name, nil
178
	}
179
	return "", errors.New("guild not found")
180
}
181
182
// RemoveStreamer removes streamer from list
183
func (t *Twitch) RemoveStreamer(login, guild string) error {
184
	complete := false
185
	if g, ok := t.Guilds[guild]; ok {
186
		for i, s := range g.Streams {
187
			if s.Guild == guild && s.Login == login {
188
				t.DB.RemoveStream(s)
189
				t.Guilds[guild].Streams[i] = t.Guilds[guild].Streams[len(t.Guilds[guild].Streams)-1]
190
				t.Guilds[guild].Streams[len(t.Guilds[guild].Streams)-1] = nil
191
				t.Guilds[guild].Streams = t.Guilds[guild].Streams[:len(t.Guilds[guild].Streams)-1]
192
				complete = true
193
			}
194
		}
195
	} else {
196
		return errors.New("guild not found")
197
	}
198
	if !complete {
199
		return errors.New("streamer not found")
200
	}
201
	return nil
202
}
203