main.commandHandler   D
last analyzed

Complexity

Conditions 12

Size

Total Lines 71
Code Lines 60

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 12
eloc 60
nop 2
dl 0
loc 71
rs 4.6689
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like main.commandHandler often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
package main
2
3
import (
4
	"bytes"
5
	"fmt"
6
	"log"
7
	"net/http"
8
	"net/url"
9
	"os"
10
	"os/signal"
11
	"strings"
12
	"syscall"
13
	"time"
14
15
	"gopkg.in/robfig/cron.v2"
16
17
	"github.com/FlameInTheDark/dtbot/bot"
18
	"github.com/FlameInTheDark/dtbot/cmd"
19
	"github.com/bwmarrin/discordgo"
20
)
21
22
var (
23
	conf *bot.Config
24
	// CmdHandler bot command handler.
25
	CmdHandler *bot.CommandHandler
26
	// Sessions bot session manager
27
	Sessions        *bot.SessionManager
28
	botId           string
0 ignored issues
show
introduced by
var botId should be botID
Loading history...
29
	youtube         *bot.Youtube
30
	botMsg          *bot.BotMessages
31
	dataType        *bot.DataType
32
	dbWorker        *bot.DBWorker
33
	guilds          *bot.GuildsMap
34
	botCron         *cron.Cron
35
	twitch          *bot.Twitch
36
	albUpdater      *bot.AlbionUpdater
37
	blacklist       *bot.BlackListStruct
38
	messagesCounter int
39
)
40
41
func main() {
42
	botCron = cron.New()
43
	conf = bot.LoadConfig()
44
	CmdHandler = bot.NewCommandHandler()
45
	registerCommands()
46
	Sessions = bot.NewSessionManager()
47
	youtube = &bot.Youtube{Conf: conf}
48
	botMsg = bot.NewMessagesMap()
49
	dataType = bot.NewDataType()
50
	discord, err := discordgo.New("Bot " + os.Getenv("BOT_TOKEN"))
51
	if err != nil {
52
		fmt.Println("Create session error, ", err)
53
		return
54
	}
55
	usr, err := discord.User("@me")
56
	if err != nil {
57
		fmt.Println("Error obtaining account details,", err)
58
		return
59
	}
60
	botId = usr.ID
61
	discord.AddHandler(func(discord *discordgo.Session, ready *discordgo.Ready) {
62
		_ = discord.UpdateStatus(0, conf.General.Game)
63
		guilds := discord.State.Guilds
64
		fmt.Println("Ready with", len(guilds), "guilds.")
65
	})
66
67
	err = discord.Open()
68
	if err != nil {
69
		fmt.Printf("Connection open error: %v", err)
70
		return
71
	}
72
	defer discord.Close()
73
	fmt.Println("Bot is now running.")
74
75
	sc := make(chan os.Signal, 1)
76
	signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt)
77
	dbWorker = bot.NewDBSession(conf.General.DatabaseName)
78
	guilds = dbWorker.InitGuilds(discord, conf)
79
	botCron.Start()
80
	defer botCron.Stop()
81
	defer dbWorker.DBSession.Close()
82
	twitch = bot.TwitchInit(discord, conf, dbWorker)
83
	albUpdater = bot.AlbionGetUpdater(dbWorker)
84
	blacklist = dbWorker.GetBlacklist()
85
	go BotUpdater(discord)
86
	// Init command handler
87
	discord.AddHandler(guildAddHandler)
88
	discord.AddHandler(commandHandler)
89
	discord.AddHandler(joinHandler)
90
	onStart()
91
	<-sc
92
}
93
94
// Handle new users
95
func joinHandler(discord *discordgo.Session, e *discordgo.GuildMemberAdd) {
96
	if _, ok := guilds.Guilds[e.GuildID]; !ok {
97
		dbWorker.InitNewGuild(e.GuildID, conf, guilds)
98
	} else {
99
		bot.Greetings(discord, e, guilds.Guilds[e.GuildID])
100
	}
101
}
102
103
// Handle new guilds
104
func guildAddHandler(discord *discordgo.Session, e *discordgo.GuildCreate) {
105
	if _, ok := guilds.Guilds[e.ID]; !ok {
106
		dbWorker.InitNewGuild(e.ID, conf, guilds)
107
	}
108
	emb := bot.NewEmbed("").
109
		Field(conf.GetLocaleLang("bot_joined_title", guilds.Guilds[e.ID].Language), conf.GetLocaleLang("bot_joined_text", guilds.Guilds[e.ID].Language), false)
110
	_, _ = discord.ChannelMessageSendEmbed(e.OwnerID, emb.GetEmbed())
111
}
112
113
// Handle discord messages
114
func commandHandler(discord *discordgo.Session, message *discordgo.MessageCreate) {
115
	if blacklist.CheckGuild(message.GuildID) || blacklist.CheckUser(message.Author.ID) {
116
		return
117
	}
118
	messagesCounter++
119
	user := message.Author
120
	if user.ID == botId || user.Bot {
121
		return
122
	}
123
	args := strings.Split(message.Content, " ")
124
	name := strings.ToLower(args[0])
125
	command, found := CmdHandler.Get(name)
126
	if !found {
127
		return
128
	}
129
130
	var permission = true
131
	var msg string
132
	// Checking permissions
133
	perm, err := discord.State.UserChannelPermissions(botId, message.ChannelID)
134
	if err != nil {
135
		msg = fmt.Sprintf("Error whilst getting bot permissions %v\n", err)
136
		permission = false
137
	} else {
138
		if perm&discordgo.PermissionSendMessages != discordgo.PermissionSendMessages ||
139
			perm&discordgo.PermissionAttachFiles != discordgo.PermissionAttachFiles {
140
			msg = "Permissions denied"
141
			permission = false
142
		}
143
	}
144
145
	channel, err := discord.State.Channel(message.ChannelID)
146
	if err != nil {
147
		fmt.Println("Error getting channel,", err)
148
		return
149
	}
150
	guild, err := discord.State.Guild(channel.GuildID)
151
	if err != nil {
152
		fmt.Println("Error getting guild,", err)
153
		return
154
	}
155
156
	if permission {
157
		ctx := bot.NewContext(
158
			botId,
159
			discord,
160
			guild,
161
			channel,
162
			user,
163
			message,
164
			conf,
165
			CmdHandler,
166
			Sessions,
167
			youtube,
168
			botMsg,
169
			dataType,
170
			dbWorker,
171
			guilds,
172
			botCron,
173
			twitch,
174
			albUpdater,
175
			blacklist)
176
		ctx.Args = args[1:]
177
		c := *command
178
		c(*ctx)
179
	} else {
180
		dbWorker.Log("Message", guild.ID, msg)
181
		query := []byte(fmt.Sprintf("logs,server=%v module=\"%v\"", guild.ID, "message"))
182
		addr := fmt.Sprintf("%v/write?db=%v", conf.Metrics.Address, conf.Metrics.Database)
183
		r := bytes.NewReader(query)
184
		_, _ = http.Post(addr, "", r)
185
	}
186
}
187
188
// Adds bot commands
189
func registerCommands() {
190
	CmdHandler.Register("!r", cmd.RadioCommand)
191
	CmdHandler.Register("!w", cmd.WeatherCommand)
192
	CmdHandler.Register("!ww", cmd.WeatherWeekCommand)
193
	CmdHandler.Register("!t", cmd.TranslateCommand)
194
	CmdHandler.Register("!n", cmd.NewsCommand)
195
	CmdHandler.Register("!c", cmd.CurrencyCommand)
196
	CmdHandler.Register("!y", cmd.YoutubeCommand)
197
	CmdHandler.Register("!v", cmd.VoiceCommand)
198
	CmdHandler.Register("!b", cmd.BotCommand)
199
	CmdHandler.Register("!play", cmd.YoutubeShortCommand)
200
	CmdHandler.Register("!d", cmd.DebugCommand)
201
	CmdHandler.Register("!p", cmd.PollCommand)
202
	CmdHandler.Register("!m", cmd.YandexmapCommand)
203
	CmdHandler.Register("!dice", cmd.DiceCommand)
204
	CmdHandler.Register("!help", cmd.HelpCommand)
205
	CmdHandler.Register("!cron", cmd.CronCommand)
206
	CmdHandler.Register("!geoip", cmd.GeoIPCommand)
207
	CmdHandler.Register("!twitch", cmd.TwitchCommand)
208
	CmdHandler.Register("!greetings", cmd.GreetingsCommand)
209
	CmdHandler.Register("!alb", cmd.AlbionCommand)
210
	CmdHandler.Register("!slap", cmd.SlapCommand)
211
	CmdHandler.Register("!fu", cmd.FUCommand)
212
}
213
214
// MetricsSender sends metrics to InfluxDB and another services
0 ignored issues
show
introduced by
comment on exported function BotUpdater should be of the form "BotUpdater ..."
Loading history...
215
func BotUpdater(d *discordgo.Session) {
216
	for {
217
		var vregions = make(map[string]int)
218
		go twitch.Update()
219
		go albUpdater.Update(d, dbWorker, conf)
220
		go clearSessions(d, Sessions)
221
		// Calculating users count
222
		usersCount := 0
223
		for _, g := range d.State.Guilds {
224
			vregions[g.Region] += 1
0 ignored issues
show
introduced by
should replace vregions[g.Region] += 1 with vregions[g.Region]++
Loading history...
225
			if !blacklist.CheckGuild(g.ID) {
226
				usersCount += g.MemberCount
227
			}
228
		}
229
		// Metrics counters
230
		queryCounters := []byte(fmt.Sprintf("counters guilds=%d,messages=%d,users=%d,voices=%d", len(d.State.Guilds), messagesCounter, usersCount, Sessions.Count()))
231
		addrCounters := fmt.Sprintf("%v/write?db=%v&u=%v&p=%v",
232
			conf.Metrics.Address, conf.Metrics.Database, conf.Metrics.User, conf.Metrics.Password)
233
		rCounters := bytes.NewReader(queryCounters)
234
		_, _ = http.Post(addrCounters, "", rCounters)
235
236
		// Voice region metrics
237
		for r, c := range vregions {
238
			queryCounters := []byte(fmt.Sprintf("region_%s count=%d", r, c))
239
			addrCounters := fmt.Sprintf("%v/write?db=%v&u=%v&p=%v",
240
				conf.Metrics.Address, conf.Metrics.Database, conf.Metrics.User, conf.Metrics.Password)
241
			rCounters := bytes.NewReader(queryCounters)
242
			_, _ = http.Post(addrCounters, "", rCounters)
243
		}
244
245
		// Bot lists
246
		if conf.DBL.Token != "" {
247
			sendDBL(conf.DBL.BotID, conf.DBL.Token, len(d.State.Guilds))
248
		}
249
		messagesCounter = 0
250
		time.Sleep(time.Minute)
251
	}
252
}
253
254
func sendDBL(botID, token string, guilds int) {
255
	client := &http.Client{
256
		Timeout: time.Duration(1) * time.Second,
257
	}
258
	query := url.Values{}
259
	query.Add("server_count", fmt.Sprintf("%v", guilds))
260
	req, _ := http.NewRequest("POST",
261
		fmt.Sprintf("https://discordbots.org/api/bots/%v/stats",
262
			botID), strings.NewReader(query.Encode()))
263
	req.Header.Add("Authorization", token)
264
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
265
	_, _ = client.Do(req)
266
}
267
268
func onStart() {
269
	queryCounters := []byte(fmt.Sprintf("starts value=1"))
270
	addrCounters := fmt.Sprintf("%v/write?db=%v&u=%v&p=%v",
271
		conf.Metrics.Address, conf.Metrics.Database, conf.Metrics.User, conf.Metrics.Password)
272
	rCounters := bytes.NewReader(queryCounters)
273
	_, _ = http.Post(addrCounters, "", rCounters)
274
}
275
276
func clearSessions(d *discordgo.Session, s *bot.SessionManager) {
277
	gids := s.GetGuilds()
278
	for _, gid := range gids {
279
		cs := s.GetByGuild(gid)
280
		g, gerr := d.Guild(gid)
281
		if gerr != nil {
282
			continue
283
		}
284
		var ok bool
285
		for _, vs := range g.VoiceStates {
286
			if vs.ChannelID == cs.ChannelID {
287
				if vs.UserID != botId {
288
					ok = true
289
				}
290
			}
291
		}
292
		if !ok && cs != nil {
293
			s.Leave(d, *cs)
294
		}
295
	}
296
297
	for _, sess := range s.GetAll() {
298
		if !sess.IsOk() {
299
			s.Leave(d, *sess)
300
		}
301
	}
302
303
	for i,vc := range d.VoiceConnections {
304
		if _,b := s.GetByChannel(vc.ChannelID); b == false {
305
			err := vc.Disconnect()
306
			if err != nil {
307
				log.Println(err)
308
			}
309
			vc.Close()
310
			delete(d.VoiceConnections, i)
311
		}
312
	}
313
}