bot.*Context.AlbionShowKills   B
last analyzed

Complexity

Conditions 7

Size

Total Lines 41
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 37
nop 0
dl 0
loc 41
rs 7.592
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 i, p := range players {
305
		updater.Players[p.UserID] = &players[i]
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
				fmt.Println("Getting player kills error: ", err.Error())
358
				return
359
			}
360
			var newKillTime int64
361
			for i, k := range kills {
362
				killTime, err := time.Parse(time.RFC3339Nano, k.TimeStamp)
363
				if err != nil {
364
					fmt.Println("Kill time parse error: ", err.Error())
365
					worker.Log("albion", "", fmt.Sprintf("Parse time error: %v", err.Error()))
366
					continue
367
				}
368
				if killTime.Unix() > lastTime.Unix() {
369
					if killTime.Unix() > newKillTime {
370
						newKillTime = killTime.Unix()
371
					}
372
					go SendKill(session, conf, &kills[i], p.UserID, p.Language)
373
				}
374
			}
375
			if newKillTime > lastTime.Unix() {
376
				worker.UpdateAlbionPlayerLast(p.UserID, newKillTime)
377
				u.Players[p.UserID].LastKill = newKillTime
378
			}
379
		}
380
	}
381
}
382
383
// AlbionAddPlayer adds player to updater
384
func (ctx *Context) AlbionAddPlayer() error {
385
	if len(ctx.Args) > 1 {
386
		search, err := AlbionSearchPlayers(ctx.Args[1])
387
		if err != nil {
388
			ctx.Log("albion", "", fmt.Sprintf("Searching player error: %v", err.Error()))
389
			return errors.New("error searching Albion player")
390
		}
391
		if _, ok := ctx.Albion.Players[ctx.User.ID]; !ok {
392
			kills, err := AlbionGetPlayerKills(search.Players[0].ID)
393
			if err != nil {
394
				ctx.Log("albion", "", fmt.Sprintf("Getting kills error: %v", err.Error()))
395
				return errors.New("error getting Albion kills")
396
			}
397
			var lastKill int64
398
			for _, k := range kills {
399
				killTime, err := time.Parse(time.RFC3339Nano, k.TimeStamp)
400
				if err != nil {
401
					continue
402
				}
403
				if killTime.Unix() > lastKill {
404
					lastKill = killTime.Unix()
405
				}
406
			}
407
			player := &AlbionPlayerUpdater{search.Players[0].ID, ctx.User.ID, ctx.GuildConf().Language, lastKill, time.Now().Unix()}
408
			ctx.Albion.Players[ctx.User.ID] = player
409
			ctx.DB.AddAlbionPlayer(player)
410
			return nil
411
		}
412
	}
413
	return errors.New("error")
414
}
415