1212from ast import literal_eval
1313
1414
15+ class ENUMS :
16+ BOOST_SUCCESS = 0
17+ BOOSTING_YOURSELF_FORBIDDEN = 1
18+ BOOST_NOT_EXPIRED = 2
19+
20+
1521class DiscordBot :
1622 def __init__ (self ,
1723 db_conn ,
@@ -68,13 +74,23 @@ def _dict_factory(cursor, row):
6874
6975 cur .execute ('''
7076 CREATE TABLE IF NOT EXISTS settings (
71- gid INTEGER NOT NULL,
72- skey TEXT NOT NULL,
73- svalue TEXT NOT NULL,
77+ gid INTEGER NOT NULL,
78+ skey TEXT NOT NULL,
79+ svalue TEXT NOT NULL,
7480 PRIMARY KEY (gid, skey)
7581 );
7682 ''' )
7783
84+ cur .execute ('''
85+ CREATE TABLE IF NOT EXISTS boosts (
86+ uid INTEGER NOT NULL,
87+ gid INTEGER NOT NULL,
88+ boostedid INTEGER NOT NULL,
89+ expires INTEGER NOT NULL,
90+ PRIMARY KEY (uid, gid)
91+ );
92+ ''' )
93+
7894 self .db_conn .commit ()
7995
8096 self .bot = commands .Bot (command_prefix = command_prefix ,
@@ -201,6 +217,39 @@ async def search_text_channel(ctx, search):
201217 return channel
202218 return None
203219
220+ async def xp_multiplier_adds (self , member_id , guild_id ):
221+ xp_adds = 0
222+ cur = self .db_conn .cursor ()
223+ cur .execute ('SELECT COUNT(*) as count FROM boosts WHERE gid=? AND boostedid=? AND expires>?' ,
224+ (guild_id , member_id , time .time (),))
225+ xp_adds += cur .fetchone ()['count' ] * await self .get_setting (guild_id , 'BOOST_ADD_XP_MULTIPLIER' )
226+ return xp_adds
227+
228+ async def get_all_boosts (self , member ):
229+ cur = self .db_conn .cursor ()
230+ cur .execute ('SELECT * FROM boosts WHERE gid=?' , (member .guild .id ,))
231+ return cur .fetchall ()
232+
233+ async def get_boost_user (self , member ):
234+ cur = self .db_conn .cursor ()
235+ cur .execute ('SELECT * FROM boosts WHERE uid=? AND gid=?' , (member .id , member .guild .id ,))
236+ return cur .fetchone ()
237+
238+ async def set_boost_user (self , member , user_to_boost ):
239+ if member .id == user_to_boost .id :
240+ return ENUMS .BOOSTING_YOURSELF_FORBIDDEN
241+ previous_boost = await self .get_boost_user (member )
242+ current_time = int (time .time ())
243+ if previous_boost is None or previous_boost ['expires' ] < current_time :
244+ cur = self .db_conn .cursor ()
245+ cur .execute ('INSERT OR REPLACE INTO boosts(uid, gid, boostedid, expires)'
246+ 'VALUES(?, ?, ?, ?);' ,
247+ (member .id , member .guild .id , user_to_boost .id , current_time
248+ + (await self .get_setting (member .guild .id , 'BOOST_EXPIRES_DAYS' )) * 24 * 60 * 60 ,))
249+ self .db_conn .commit ()
250+ return ENUMS .BOOST_SUCCESS
251+ return ENUMS .BOOST_NOT_EXPIRED
252+
204253 async def get_users (self , guild ):
205254 cur = self .db_conn .cursor ()
206255 cur .execute ('SELECT * FROM users WHERE gid=?' , (guild .id ,))
@@ -310,7 +359,8 @@ async def update_all_voice_users(self, ctime):
310359 else :
311360 xp_earned = self .xp_for ((ctime - user ['joined' ])
312361 * await self .get_setting (guild .id , 'VOICE_XP_PER_MINUTE' ) / 60 ,
313- user ['xp_multiplier' ])
362+ user ['xp_multiplier' ]
363+ + await self .xp_multiplier_adds (user ['uid' ], user ['gid' ]))
314364
315365 user ['joined' ] = ctime
316366
@@ -343,11 +393,9 @@ async def member_create_profile_image(self, member):
343393 # Retrieve user data
344394 #
345395 data = await self .get_user (member )
346- name = member .name
347- if member .nick is not None :
348- name = member .nick
396+ name = member .display_name
349397
350- data_xp_multiplier = data ['xp_multiplier' ]
398+ data_xp_multiplier = data ['xp_multiplier' ] + await self . xp_multiplier_adds ( data [ 'uid' ], data [ 'gid' ])
351399 data_xp = int (self .lvl_get_xp (data ['lvl' ]))
352400 data_max_xp = int (self .max_xp_for (data ['lvl' ]))
353401 data_percentage = data_xp / data_max_xp
@@ -368,10 +416,18 @@ async def member_create_profile_image(self, member):
368416 (await self .get_setting (member .guild .id , 'PROFILE_IMAGE' ))(data_obj ))
369417 return discord .File (filename = "member.png" , fp = img_buf )
370418
419+ async def member_create_welcome_image (self , member ):
420+ name = member .display_name
421+ data_obj = {'member' : member ,
422+ 'name' : name ,
423+ 'avatar_url' : str (member .avatar_url_as (format = "png" )),
424+ 'guild_icon_url' : str (member .guild .icon_url_as (format = "png" ))}
425+ img_buf = await self .image_creator .create (
426+ (await self .get_setting (member .guild .id , 'WELCOME_IMAGE' ))(data_obj ))
427+ return discord .File (filename = "welcome.png" , fp = img_buf )
428+
371429 async def member_create_lvl_image (self , member , old_lvl , new_lvl ):
372- name = member .name
373- if member .nick is not None :
374- name = member .nick
430+ name = member .display_name
375431
376432 data_obj = {'member' : member ,
377433 'old_lvl' : self .get_lvl (old_lvl ),
@@ -384,9 +440,7 @@ async def member_create_lvl_image(self, member, old_lvl, new_lvl):
384440 return discord .File (filename = "lvlup.png" , fp = img_buf )
385441
386442 async def member_create_rank_up_image (self , member , old_lvl , new_lvl , old_role , new_role ):
387- name = member .name
388- if member .nick is not None :
389- name = member .nick
443+ name = member .display_name
390444
391445 data_obj = {'member' : member ,
392446 'old_lvl' : self .get_lvl (old_lvl ),
@@ -406,9 +460,7 @@ async def create_ranking_image(self, member, ranked_users):
406460 for user in ranked_users :
407461 member = get (member .guild .members , id = int (user ['uid' ]))
408462 if member is not None and not member .bot :
409- name = member .name
410- if member .nick is not None :
411- name = member .nick
463+ name = member .display_name
412464 data_obj .append ({
413465 'member' : member ,
414466 'rank' : user ['rank' ],
@@ -480,7 +532,7 @@ async def member_left_vc(self, member, t):
480532 data = await self .get_user (member )
481533 if bool (data ['blacklist' ]) is True :
482534 return
483- xp_multiplier = data ['xp_multiplier' ]
535+ xp_multiplier = data ['xp_multiplier' ] + await self . xp_multiplier_adds ( data [ 'uid' ], data [ 'gid' ])
484536 if data ['joined' ] >= 0 :
485537 xp_earned = self .xp_for ((t - data ['joined' ])
486538 * await self .get_setting (member .guild .id , 'VOICE_XP_PER_MINUTE' ) / 60 , xp_multiplier )
@@ -494,7 +546,7 @@ async def member_message_xp(self, member):
494546 data = await self .get_user (member )
495547 if bool (data ['blacklist' ]) is True :
496548 return
497- xp_multiplier = data ['xp_multiplier' ]
549+ xp_multiplier = data ['xp_multiplier' ] + await self . xp_multiplier_adds ( data [ 'uid' ], data [ 'gid' ])
498550 xp_earned = self .xp_for (await self .get_setting (member .guild .id , 'MESSAGE_XP' ), xp_multiplier )
499551 old_level = data ['lvl' ]
500552 data ['lvl' ] += self .lvl_xp_add (xp_earned , data ['lvl' ])
@@ -600,6 +652,63 @@ async def _profile(self, ctx, *args):
600652 description = 'No user was found!' ,
601653 color = discord .Color .red ()))
602654
655+ @commands .command (name = 'boost' ,
656+ aliases = ['b' ],
657+ description = 'give a friend a boost' ,
658+ help = ' - Boosted einen Freund' )
659+ async def _boost (self , ctx , * args ):
660+ if ctx .guild is None :
661+ raise commands .NoPrivateMessage ()
662+ await ctx .trigger_typing ()
663+ if len (args ) == 0 :
664+ current_time = time .time ()
665+
666+ boosting = await self .parent .get_boost_user (ctx .message .author )
667+
668+ if boosting is None or boosting ['expires' ] < current_time :
669+ return await ctx .send (embed = discord .Embed (title = 'Boost' ,
670+ description = 'You currently boosting no one!\n '
671+ 'Use "boost {member} to start boosting!' ,
672+ color = discord .Color .gold ()))
673+
674+ member = get (ctx .message .guild .members , id = int (boosting ['boostedid' ]))
675+
676+ member_name = 'A USER WHO LEFT'
677+
678+ if member is not None :
679+ member_name = member .display_name
680+
681+ boost_remaining = (boosting ['expires' ] - current_time ) / (24 * 60 * 60 )
682+ boost_remaining_days = round (boost_remaining )
683+ boost_remaining_hours = round ((boost_remaining - boost_remaining_days ) * 24 )
684+ return await ctx .send (embed = discord .Embed (title = 'Boost' ,
685+ description = 'You are boosting {}!\n '
686+ 'Boost expires in {} days and {} hours!'
687+ .format (member_name ,
688+ boost_remaining_days ,
689+ boost_remaining_hours ),
690+ color = discord .Color .gold ()))
691+
692+ member = await self .parent .search_member (ctx , ' ' .join (args ))
693+ if member is None :
694+ return await ctx .send (embed = discord .Embed (title = '' ,
695+ description = 'No matching user was found' ,
696+ color = discord .Color .red ()))
697+
698+ res = await self .parent .set_boost_user (ctx .message .author , member )
699+ if res == ENUMS .BOOST_SUCCESS :
700+ return await ctx .send (embed = discord .Embed (title = '' ,
701+ description = 'You are boosting {} now!'
702+ .format (member .display_name ),
703+ color = discord .Color .green ()))
704+ elif res == ENUMS .BOOSTING_YOURSELF_FORBIDDEN :
705+ return await ctx .send (embed = discord .Embed (title = '' ,
706+ description = 'You cannot boost yourself!' ,
707+ color = discord .Color .red ()))
708+ return await ctx .send (embed = discord .Embed (title = '' ,
709+ description = 'Your boost has not expired yet!' ,
710+ color = discord .Color .red ()))
711+
603712 @commands .command (name = 'leaderboard' ,
604713 aliases = [],
605714 description = 'show the leaderboard' ,
@@ -638,7 +747,7 @@ async def _send(self, ctx, *args):
638747 if len (args ) == 0 :
639748 return await ctx .send (embed = discord .Embed (title = 'Help' ,
640749 description = '"send {msg}" to send into the system channel\n '
641- '"send {channel_id} {msg}" to send'
750+ '"send {channel_id} {msg}" to send '
642751 'into a specific channel' ,
643752 color = discord .Color .gold ()))
644753 elif len (args ) == 1 :
@@ -702,6 +811,9 @@ async def __setlvl(m):
702811 help = ' - Alles ein-zu-stellen' )
703812 @commands .has_permissions (administrator = True )
704813 async def _settings (self , ctx , * args ):
814+ if ctx .guild is None :
815+ raise commands .NoPrivateMessage ()
816+
705817 if len (args ) == 0 :
706818 pass
707819 elif args [0 ] in ['set' , 's' ] and len (args ) >= 3 :
@@ -763,6 +875,7 @@ async def _settings(self, ctx, *args):
763875 async def _lvlsys (self , ctx , * args ):
764876 if ctx .guild is None :
765877 raise commands .NoPrivateMessage ()
878+
766879 await ctx .trigger_typing ()
767880
768881 embed = discord .Embed (title = 'Help' ,
@@ -868,6 +981,9 @@ async def __blacklist(m):
868981 help = ' - Löscht Nachrichten in einem Text-Channel!' )
869982 @commands .has_permissions (administrator = True )
870983 async def _clear (self , ctx , * args ):
984+ if ctx .guild is None :
985+ raise commands .NoPrivateMessage ()
986+
871987 await ctx .trigger_typing ()
872988 if len (args ) == 0 :
873989 pass
@@ -973,6 +1089,9 @@ async def on_command_error(self, ctx, error):
9731089 await ctx .send (embed = discord .Embed (description = random .choice (
9741090 await self .parent .get_setting (ctx .message .author .guild .id , 'MISSING_PERMISSIONS_RESPONSES' )),
9751091 color = discord .Color .red ()))
1092+
1093+ elif isinstance (error , commands .NoPrivateMessage ):
1094+ await ctx .send ('This command does not work via Private Message' )
9761095 else :
9771096 self .parent .lprint (error )
9781097
@@ -984,8 +1103,8 @@ async def on_ready(self):
9841103 async def on_member_join (self , member ):
9851104 if not member .bot :
9861105 await self .parent .update_member (member )
987- # send a private message on join
988- # await member.send('Private message' )
1106+ if await self . parent . get_setting ( member . guild . id , 'SEND_WELCOME_IMAGE' ):
1107+ await member .send (file = await self . parent . member_create_welcome_image ( member ) )
9891108
9901109 @commands .Cog .listener ()
9911110 async def on_member_remove (self , member ):
0 commit comments