-
Notifications
You must be signed in to change notification settings - Fork 0
/
bot-skel.py
188 lines (155 loc) · 5.98 KB
/
bot-skel.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
#!./.venv/bin/python
import discord # base discord module
import code # code.interact
import os # environment variables
import inspect # call stack inspection
import random
import os
from discord.channel import VoiceChannel
from discord.ext import commands # Bot class and utils
################################################################################
############################### HELPER FUNCTIONS ###############################
################################################################################
# log_msg - fancy print
# @msg : string to print
# @level : log level from {'debug', 'info', 'warning', 'error'}
def log_msg(msg: str, level: str):
# user selectable display config (prompt symbol, color)
dsp_sel = {
'debug' : ('\033[34m', '-'),
'info' : ('\033[32m', '*'),
'warning' : ('\033[33m', '?'),
'error' : ('\033[31m', '!'),
}
# internal ansi codes
_extra_ansi = {
'critical' : '\033[35m',
'bold' : '\033[1m',
'unbold' : '\033[2m',
'clear' : '\033[0m',
}
# get information about call site
caller = inspect.stack()[1]
# input sanity check
if level not in dsp_sel:
print('%s%s[@] %s:%d %sBad log level: "%s"%s' % \
(_extra_ansi['critical'], _extra_ansi['bold'],
caller.function, caller.lineno,
_extra_ansi['unbold'], level, _extra_ansi['clear']))
return
# print the damn message already
print('%s%s[%s] %s:%d %s%s%s' % \
(_extra_ansi['bold'], *dsp_sel[level],
caller.function, caller.lineno,
_extra_ansi['unbold'], msg, _extra_ansi['clear']))
################################################################################
############################## BOT IMPLEMENTATION ##############################
################################################################################
song_list = {
'song1.mp3' : '1',
'song2.mp3' : '2',
'song3.mp3' : '3'
}
# bot instantiation
bot = commands.Bot(command_prefix='*')
# on_ready - called after connection to server is established
@bot.event
async def on_ready():
log_msg('logged on as <%s>' % bot.user, 'info')
# on_message - called when a new message is posted to the server
# @msg : discord.message.Message
@bot.event
async def on_message(msg):
# filter out our own messages
if msg.author == bot.user:
return
log_msg('message from <%s>: "%s"' % (msg.author, msg.content), 'debug')
# overriding the default on_message handler blocks commands from executing
# manually call the bot's command processor on given message
await bot.process_commands(msg)
#code.interact(local=dict(globals(), **locals()))
@bot.event
async def on_voice_state_update(member, before, after):
voice_state = member.guild.voice_client
if voice_state is None:
return
if len(voice_state.channel.members) == 1:
await voice_state.disconnect()
@bot.command(brief='Play a song.')
# play - play music
async def play(ctx, song):
if not ctx.message.author.voice:
await ctx.send("You're not in any voice channel :(".format(ctx.message.author.name))
return
else:
channel = ctx.message.author.voice.channel
await ctx.send("Here I am.")
await channel.connect()
try :
server = ctx.message.guild
voice_channel = server.voice_client
if song in song_list:
async with ctx.typing():
voice_channel.play(discord.FFmpegPCMAudio(song))
await ctx.send('Current song: ' + song)
else :
await ctx.send("I don't know that song.")
except :
await ctx.send("Not playing anything right now.")
@bot.command()
async def list(ctx):
await ctx.send("Song list: ")
for key in song_list:
await ctx.send(song_list[key] + '. ' + key)
@bot.command(brief='The bot will leave the voice channel.')
async def scram(ctx):
voice_client = ctx.message.guild.voice_client
if voice_client.is_connected():
await voice_client.disconnect()
await ctx.send("Ok, bye...")
else:
await ctx.send("The bot is not connected to a voice channel.")
@bot.command(brief='Pause the song.')
async def pause(ctx):
voice = discord.utils.get(bot.voice_clients, guild= ctx.guild)
if voice.is_playing():
voice.pause()
await ctx.send("Paused song.")
else:
await ctx.send("Not playing anything at the moment.")
@bot.command()
async def resume(ctx):
voice = discord.utils.get(bot.voice_clients, guild= ctx.guild)
if voice.is_paused():
voice.resume()
await ctx.send("Let's resume :)")
else:
await ctx.send("I'm already playing something.")
@bot.command()
async def stop(ctx):
voice = discord.utils.get(bot.voice_clients, guild= ctx.guild)
voice.stop();
# roll - rng chat command
# @ctx : command invocation context
# @max_val : upper bound for number generation (must be at least 1)
@bot.command(brief='Generate random number between 1 and <arg>')
async def roll(ctx, max_val: int):
# argument sanity check
if max_val < 1:
raise Exception('argument <max_val> must be at least 1')
await ctx.send(random.randint(1, max_val))
# roll_error - error handler for the <roll> command
# @ctx : command that crashed invocation context
# @error : ...
@roll.error
async def roll_error(ctx, error):
await ctx.send(str(error))
################################################################################
############################# PROGRAM ENTRY POINT ##############################################################################################################
if __name__ == '__main__':
# check that token exists in environment
if 'BOT_TOKEN' not in os.environ:
log_msg('save your token in the BOT_TOKEN env variable!', 'error')
exit(-1)
# launch bot (blocking operation)
bot.run(os.environ['BOT_TOKEN'])