Passed
Push — master ( 88405e...a266ab )
by Viktor
01:47
created

bot.*AlbionUpdater.Update   C

Complexity

Conditions 9

Size

Total Lines 32
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 27
nop 3
dl 0
loc 32
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
// SearchResult contains search response
0 ignored issues
show
introduced by
comment on exported type AlbionSearchResult should be of the form "AlbionSearchResult ..." (with optional leading article)
Loading history...
14
type AlbionSearchResult struct {
15
	Guilds  []AlbionGuildSearch  `json:"guilds"`
16
	Players []AlbionPlayerSearch `json:"players"`
17
}
18
19
// GuildSearch contains guild data from search response
0 ignored issues
show
introduced by
comment on exported type AlbionGuildSearch should be of the form "AlbionGuildSearch ..." (with optional leading article)
Loading history...
20
type AlbionGuildSearch struct {
21
	ID           string `json:"Id"`
22
	Name         string `json:"Name"`
23
	AllianceID   string `json:"AllianceId"`
24
	AllianceName string `json:"AllianceName"`
25
	KillFame     int    `json:"KillFame"`
26
	DeathFame    int    `json:"DeathFame"`
27
}
28
29
// PlayerSearch contains player data from search response
0 ignored issues
show
introduced by
comment on exported type AlbionPlayerSearch should be of the form "AlbionPlayerSearch ..." (with optional leading article)
Loading history...
30
type AlbionPlayerSearch struct {
31
	ID           string  `json:"Id"`
32
	Name         string  `json:"Name"`
33
	AllianceID   string  `json:"AllianceId"`
34
	AllianceName string  `json:"AllianceName"`
35
	KillFame     int     `json:"KillFame"`
36
	DeathFame    int     `json:"DeathFame"`
37
	Avatar       string  `json:"Avatar"`
38
	AvatarRing   string  `json:"AvatarRing"`
39
	FameRation   float64 `json:"FameRatio"`
40
	TotalKills   int     `json:"totalKills"`
41
	GVGKills     int     `json:"gvgKills"`
42
	GVGWon       int     `json:"gvgWon"`
43
}
44
45
// Player data
0 ignored issues
show
introduced by
comment on exported type AlbionPlayer should be of the form "AlbionPlayer ..." (with optional leading article)
Loading history...
46
type AlbionPlayer struct {
47
	AverageItemPower float64         `json:"AverageItemPower"`
48
	Equipment        AlbionEquipment `json:"Equipment"`
49
	Inventory        []AlbionItem    `json:"Inventory"`
50
	Name             string          `json:"Name"`
51
	Id               string          `json:"Id"`
0 ignored issues
show
introduced by
struct field Id should be ID
Loading history...
52
	GuildName        string          `json:"GuildName"`
53
	GuildId          string          `json:"GuildId"`
0 ignored issues
show
introduced by
struct field GuildId should be GuildID
Loading history...
54
	AllianceName     string          `json:"AllianceName"`
55
	AllianceId       string          `json:"AllianceId"`
0 ignored issues
show
introduced by
struct field AllianceId should be AllianceID
Loading history...
56
	AllianceTag      string          `json:"AllianceTag"`
57
	Avatar           string          `json:"Avatar"`
58
	AvatarRing       string          `json:"AvatarRing"`
59
	DeathFame        int             `json:"DeathFame"`
60
	KillFame         int             `json:"KillFame"`
61
	FameRatio        float64         `json:"FameRatio"`
62
	DamageDone       float64         `json:"DamageDone"`
63
}
64
65
// Equipment contains items in slots
0 ignored issues
show
introduced by
comment on exported type AlbionEquipment should be of the form "AlbionEquipment ..." (with optional leading article)
Loading history...
66
type AlbionEquipment struct {
67
	MainHand AlbionItem `json:"MainHand"`
68
	OffHand  AlbionItem `json:"OffHand"`
69
	Head     AlbionItem `json:"Head"`
70
	Armor    AlbionItem `json:"Armor"`
71
	Shoes    AlbionItem `json:"Shoes"`
72
	Bag      AlbionItem `json:"Bag"`
73
	Cape     AlbionItem `json:"Cape"`
74
	Mount    AlbionItem `json:"Mount"`
75
	Potion   AlbionItem `json:"Potion"`
76
	Food     AlbionItem `json:"Food"`
77
}
78
79
// Item contains item data
0 ignored issues
show
introduced by
comment on exported type AlbionItem should be of the form "AlbionItem ..." (with optional leading article)
Loading history...
80
type AlbionItem struct {
81
	Type    string `json:"Type"`
82
	Count   int    `json:"Count"`
83
	Quality int    `json:"Quality"`
84
}
85
86
// Kill contains kill data
0 ignored issues
show
introduced by
comment on exported type AlbionKill should be of the form "AlbionKill ..." (with optional leading article)
Loading history...
87
type AlbionKill struct {
88
	GroupMemberCount     int            `json:"groupMemberCount"`
89
	NumberOfParticipants int            `json:"numberOfParticipants"`
90
	EventID              int            `json:"EventId"`
91
	TimeStamp            string         `json:"TimeStamp"`
92
	Version              int            `json:"Version"`
93
	Killer               AlbionPlayer   `json:"Killer"`
94
	Victim               AlbionPlayer   `json:"Victim"`
95
	TotalVictimKillFame  int            `json:"TotalVictimKillFame"`
96
	Location             string         `json:"Location"`
97
	Participants         []AlbionPlayer `json:"Participants"`
98
	GroupMembers         []AlbionPlayer `json:"GroupMembers"`
99
	BattleID             int            `json:"BattleId"`
100
	Type                 string         `json:"Type"`
101
}
102
103
type AlbionUpdater struct {
0 ignored issues
show
introduced by
exported type AlbionUpdater should have comment or be unexported
Loading history...
104
	Players map[string]*AlbionPlayerUpdater
105
}
106
107
type AlbionPlayerUpdater struct {
0 ignored issues
show
introduced by
exported type AlbionPlayerUpdater should have comment or be unexported
Loading history...
108
	PlayerID string
109
	UserID   string
110
	Language string
111
	LastKill int64
112
	StartAt  int64
113
}
114
115
// SearchPlayers returns player list by name
0 ignored issues
show
introduced by
comment on exported function AlbionSearchPlayers should be of the form "AlbionSearchPlayers ..."
Loading history...
116
func AlbionSearchPlayers(name string) (result *AlbionSearchResult, err error) {
117
	var sResult AlbionSearchResult
118
	resp, err := http.Get(fmt.Sprintf("https://gameinfo.albiononline.com/api/gameinfo/search?q=%v", name))
119
	if err != nil {
120
		return nil, err
121
	}
122
123
	if resp.StatusCode != 200 {
124
		return nil, errors.New("status " + resp.Status)
125
	}
126
127
	err = json.NewDecoder(resp.Body).Decode(&sResult)
128
	if err != nil {
129
		return nil, err
130
	}
131
132
	return &sResult, nil
133
}
134
135
// GetPlayerKills returns array of kills by player id
0 ignored issues
show
introduced by
comment on exported function AlbionGetPlayerKills should be of the form "AlbionGetPlayerKills ..."
Loading history...
136
func AlbionGetPlayerKills(id string) (result []AlbionKill, err error) {
137
	var kills []AlbionKill
138
	resp, err := http.Get(fmt.Sprintf("https://gameinfo.albiononline.com/api/gameinfo/players/%v/topkills?range=month&offset=0&limit=20", id))
139
	if err != nil {
140
		return nil, err
141
	}
142
143
	if resp.StatusCode != 200 {
144
		return nil, errors.New("status " + resp.Status)
145
	}
146
147
	err = json.NewDecoder(resp.Body).Decode(&kills)
148
	if err != nil {
149
		return nil, err
150
	}
151
152
	return kills, nil
153
}
154
155
// AlbionGetKillID returns kill by ID
156
func AlbionGetKillID(id string) (kill *AlbionKill, err error) {
157
	var result AlbionKill
158
	resp, err := http.Get(fmt.Sprintf("https://gameinfo.albiononline.com/api/gameinfo/events/%v", id))
159
	if err != nil {
160
		return nil, err
161
	}
162
163
	if resp.StatusCode != 200 {
164
		return nil, errors.New("status " + resp.Status)
165
	}
166
167
	err = json.NewDecoder(resp.Body).Decode(&result)
168
	if err != nil {
169
		return nil, err
170
	}
171
172
	return &result, nil
173
}
174
175
// ShowKills sends embed message in discord
0 ignored issues
show
introduced by
comment on exported method Context.AlbionShowKills should be of the form "AlbionShowKills ..."
Loading history...
176
func (ctx *Context) AlbionShowKills() {
177
	search, err := AlbionSearchPlayers(ctx.Args[1])
178
	if err != nil {
179
		fmt.Println("Error:" + err.Error())
180
		return
181
	}
182
	fmt.Println("Founded players")
183
	if len(search.Players) > 0 {
184
		fmt.Println("Players more then 0")
185
		kills, err := AlbionGetPlayerKills(search.Players[0].ID)
186
		fmt.Println("Searching kills of " + search.Players[0].Name + search.Players[0].ID)
187
		if err != nil {
188
			fmt.Println("Error: " + err.Error())
189
			return
190
		}
191
		fmt.Println("Founded kills of " + search.Players[0].Name)
192
		if len(kills) > 0 {
193
			fmt.Println("Kills more then 0")
194
			embed := NewEmbed("Albion Killboard")
195
			embed.Desc(fmt.Sprintf("[%v](https://albiononline.com/ru/killboard/player/%v)", search.Players[0].Name, search.Players[0].ID)) // "https://assets.albiononline.com/assets/images/icons/favicon.ico")
196
			embed.Color(ctx.GuildConf().EmbedColor)
197
			for _, k := range kills {
198
				fmt.Println("Killed " + k.Victim.Name)
199
				var timeString string
200
				t, err := time.Parse(time.RFC3339Nano, k.TimeStamp)
201
				if err == nil {
202
					timeString = fmt.Sprintf("%v.%v.%v %v:%v", t.Day(), t.Month().String(), t.Year(), t.Hour(), t.Minute())
203
				} else {
204
					fmt.Println("Parse time: ", err.Error())
205
				}
206
				embed.Field(
207
					k.Victim.Name,
208
					fmt.Sprintf("%v [[%v](https://albiononline.com/ru/killboard/kill/%v)]",
209
						fmt.Sprintf(ctx.Loc("albion_kill_short"),
0 ignored issues
show
introduced by
can't check non-constant format in call to Sprintf
Loading history...
210
							k.Victim.DeathFame,
211
							k.Victim.AverageItemPower,
212
							timeString),
213
						k.EventID,
214
						k.EventID), false)
215
			}
216
			embed.Send(ctx)
217
		}
218
219
	}
220
}
221
222
// AlbionShowKill sends kill embed to user
223
func (ctx *Context) AlbionShowKill() {
224
	kill, err := AlbionGetKillID(ctx.Args[1])
225
	if err != nil {
226
		fmt.Println("Error:" + err.Error())
227
		return
228
	}
229
230
	embed := NewEmbed(fmt.Sprintf("Show on killboard #%v", kill.EventID))
231
	embed.Desc(fmt.Sprintf("%v :crossed_swords: %v", kill.Killer.Name, kill.Victim.Name))
232
	embed.Color(ctx.GuildConf().EmbedColor)
233
	embed.URL(fmt.Sprintf("https://albiononline.com/ru/killboard/kill/%v", kill.EventID))
234
	embed.AttachThumbURL("https://assets.albiononline.com/assets/images/header/logo.png")
235
	embed.Author("Albion Killboard", "https://albiononline.com/ru/killboard", "")
236
	embed.TimeStamp(kill.TimeStamp)
237
	embed.Field(ctx.Loc("albion_guild"), kill.Victim.GuildName, true)
238
	embed.Field(ctx.Loc("albion_fame"), fmt.Sprintf("%d", kill.Victim.DeathFame), true)
239
	embed.Field(ctx.Loc("albion_item_power"), fmt.Sprintf("%.3f", kill.Victim.AverageItemPower), true)
240
	embed.Field(ctx.Loc("albion_killer_item_power"), fmt.Sprintf("%.3f", kill.Killer.AverageItemPower), true)
241
	if len(kill.Participants) > 0 {
242
		var names []string
243
		for _, p := range kill.Participants {
244
			names = append(names, fmt.Sprintf("%v (%.0f)", p.Name, p.DamageDone))
245
		}
246
		embed.Field(ctx.Loc("albion_participants"), strings.Join(names, ", "), true)
247
	}
248
	embed.Send(ctx)
249
}
250
251
// SendKill sends kill to user
252
func SendKill(session *discordgo.Session, conf *Config, kill *AlbionKill, userID, lang string) {
253
	embed := NewEmbed(fmt.Sprintf("Show on killboard #%v", kill.EventID))
254
	embed.Desc(fmt.Sprintf("%v :crossed_swords: %v", kill.Killer.Name, kill.Victim.Name))
255
	embed.Color(4460547)
256
	embed.URL(fmt.Sprintf("https://albiononline.com/ru/killboard/kill/%v", kill.EventID))
257
	embed.AttachThumbURL("https://assets.albiononline.com/assets/images/header/logo.png")
258
	embed.Author("Albion Killboard", "https://albiononline.com/ru/killboard", "")
259
	embed.TimeStamp(kill.TimeStamp)
260
	embed.Field(conf.GetLocaleLang("albion_item_power", lang), fmt.Sprintf("%.2f", kill.Victim.AverageItemPower), true)
261
	embed.Field(conf.GetLocaleLang("albion_killer_item_power", lang), fmt.Sprintf("%.2f", kill.Killer.AverageItemPower), true)
262
	embed.Field(conf.GetLocaleLang("albion_fame", lang), fmt.Sprintf("%d", kill.Victim.DeathFame), true)
263
	if kill.Victim.GuildName != "" {
264
		embed.Field(conf.GetLocaleLang("albion_guild", lang), kill.Victim.GuildName, true)
265
	}
266
	if len(kill.Participants) > 0 {
267
		var names []string
268
		for _, p := range kill.Participants {
269
			names = append(names, fmt.Sprintf("%v (%.0f)", p.Name, p.DamageDone))
270
		}
271
		participants := strings.Join(names, ", ")
272
		if len(participants) < 1000 {
273
			embed.Field(conf.GetLocaleLang("albion_participants", lang), participants, true)
274
		}
275
	}
276
	ch, err := session.UserChannelCreate(userID)
277
	if err != nil {
278
		fmt.Println("Error whilst creating private channel, ", err.Error())
279
		return
280
	}
281
	_, err = session.ChannelMessageSendEmbed(ch.ID, embed.GetEmbed())
282
	if err != nil {
283
		fmt.Println("Error whilst sending embed message, ", err.Error())
284
		return
285
	}
286
}
287
288
// GetPlayerByID returns player ID by player name
0 ignored issues
show
introduced by
comment on exported function GetPlayerByName should be of the form "GetPlayerByName ..."
Loading history...
289
func GetPlayerByName(name string) string {
290
	search, err := AlbionSearchPlayers(name)
291
	if err == nil {
292
		if len(search.Players) > 0 {
293
			return search.Players[0].ID
294
		}
295
	}
296
	return ""
297
}
298
299
// AlbionGetUpdater creates and returns albion kills updater
300
func AlbionGetUpdater(db *DBWorker) *AlbionUpdater {
301
	var updater = &AlbionUpdater{Players: make(map[string]*AlbionPlayerUpdater)}
302
	var players []AlbionPlayerUpdater
303
	players = db.GetAlbionPlayers()
304
	for _, p := range players {
305
		updater.Players[p.UserID] = &p
306
	}
307
	return updater
308
}
309
310
// SendPlayerKills sends player kills
311
func SendPlayerKills(session *discordgo.Session, worker *DBWorker, conf *Config, updater *AlbionUpdater, userID string) {
312
	startTime := time.Unix(updater.Players[userID].StartAt, 0)
313
	lastTime := time.Unix(updater.Players[userID].LastKill, 0)
314
	if startTime.Add(time.Hour * 24).Unix() < time.Now().Unix() {
315
		worker.RemoveAlbionPlayer(updater.Players[userID].UserID)
316
		delete(updater.Players, updater.Players[userID].UserID)
317
		return
318
	} else {
0 ignored issues
show
introduced by
if block ends with a return statement, so drop this else and outdent its block
Loading history...
319
		kills, err := AlbionGetPlayerKills(updater.Players[userID].PlayerID)
320
		if err != nil {
321
			return
322
		}
323
		var newKillTime int64
324
		for i, k := range kills {
325
			killTime, err := time.Parse(time.RFC3339Nano, k.TimeStamp)
326
			if err != nil {
327
				fmt.Println("Kill time parse error: ", err.Error())
328
				continue
329
			}
330
			if killTime.Unix() > lastTime.Unix() {
331
				if killTime.Unix() > newKillTime {
332
					newKillTime = killTime.Unix()
333
				}
334
				SendKill(session, conf, &kills[i], userID, updater.Players[userID].Language)
335
			}
336
		}
337
		if newKillTime > lastTime.Unix() {
338
			worker.UpdateAlbionPlayerLast(userID, newKillTime)
339
			updater.Players[userID].LastKill = newKillTime
340
		}
341
	}
342
}
343
344
// Update updates players kills and sends to users
345
func (u *AlbionUpdater) Update(session *discordgo.Session, worker *DBWorker, conf *Config) {
346
	for _, p := range u.Players {
347
		startTime := time.Unix(p.StartAt, 0)
348
		lastTime := time.Unix(p.LastKill, 0)
349
		if startTime.Add(time.Hour * 24).Unix() < time.Now().Unix() {
350
			worker.RemoveAlbionPlayer(p.UserID)
351
			delete(u.Players, p.UserID)
352
			return
353
		} else {
0 ignored issues
show
introduced by
if block ends with a return statement, so drop this else and outdent its block
Loading history...
354
			kills, err := AlbionGetPlayerKills(p.PlayerID)
355
			if err != nil {
356
				worker.Log("albion", "", fmt.Sprintf("Getting player kills error: %v", err.Error()))
357
				return
358
			}
359
			var newKillTime int64
360
			for i, k := range kills {
361
				killTime, err := time.Parse(time.RFC3339Nano, k.TimeStamp)
362
				if err != nil {
363
					fmt.Println("Kill time parse error: ", err.Error())
364
					worker.Log("albion", "", fmt.Sprintf("Parse time error: %v", err.Error()))
365
					continue
366
				}
367
				if killTime.Unix() > lastTime.Unix() {
368
					if killTime.Unix() > newKillTime {
369
						newKillTime = killTime.Unix()
370
					}
371
					go SendKill(session, conf, &kills[i], p.UserID, p.Language)
372
				}
373
			}
374
			if newKillTime > lastTime.Unix() {
375
				worker.UpdateAlbionPlayerLast(p.UserID, newKillTime)
376
				u.Players[p.UserID].LastKill = newKillTime
377
			}
378
		}
379
	}
380
}
381
382
// AlbionAddPlayer adds player to updater
383
func (ctx *Context) AlbionAddPlayer() error {
384
	if len(ctx.Args) > 1 {
385
		search, err := AlbionSearchPlayers(ctx.Args[1])
386
		if err != nil {
387
			ctx.Log("albion", "", fmt.Sprintf("Searching player error: %v", err.Error()))
388
			return errors.New("error searching Albion player")
389
		}
390
		if _, ok := ctx.Albion.Players[ctx.User.ID]; !ok {
391
			kills, err := AlbionGetPlayerKills(search.Players[0].ID)
392
			if err != nil {
393
				ctx.Log("albion", "", fmt.Sprintf("Getting kills error: %v", err.Error()))
394
				return errors.New("error getting Albion kills")
395
			}
396
			var lastKill int64
397
			for _, k := range kills {
398
				killTime, err := time.Parse(time.RFC3339Nano, k.TimeStamp)
399
				if err != nil {
400
					continue
401
				}
402
				if killTime.Unix() > lastKill {
403
					lastKill = killTime.Unix()
404
				}
405
			}
406
			player := &AlbionPlayerUpdater{search.Players[0].ID, ctx.User.ID, ctx.GuildConf().Language, lastKill, time.Now().Unix()}
407
			ctx.Albion.Players[ctx.User.ID] = player
408
			ctx.DB.AddAlbionPlayer(player)
409
			return nil
410
		}
411
	}
412
	return errors.New("error")
413
}
414