Issues (115)

bot/albion.go (19 issues)

Severity
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
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
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
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
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
struct field Id should be ID
Loading history...
52
	GuildName        string          `json:"GuildName"`
53
	GuildId          string          `json:"GuildId"`
0 ignored issues
show
struct field GuildId should be GuildID
Loading history...
54
	AllianceName     string          `json:"AllianceName"`
55
	AllianceId       string          `json:"AllianceId"`
0 ignored issues
show
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
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
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
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
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
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
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
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
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
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
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
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
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