Passed
Push — master ( 7e37ba...77df28 )
by Viktor
01:31
created

bot.*Twitch.RemoveStreamer   B

Complexity

Conditions 7

Size

Total Lines 19
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

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