-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbot.py
154 lines (127 loc) · 5.42 KB
/
bot.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
import asyncio
import os
import uuid
from discord import Member, Status, Activity, ActivityType
from discord.ext import commands
# Constants (coming from env variables)
BOT_TOKEN = os.environ['CHAT_BOT_SECRET'] # REQUIRED
VOICE_CHANNEL_CAT = os.environ['CHAT_VOICE_CAT'] # REQUIRED
NUM_PARTICIPANTS = int(os.environ.get('CHAT_NUM_PARTICIPANTS', 5)) # OPTIONAL
CHANNEL_ID = int(os.environ.get('CHAT_CHANNEL_ID', None)) # OPTIONAL
HELP_MSG_DELAY = int(os.environ.get('CHAT_HELP_DELAY', 600)) # OPTIONAL
CAN_CREATE_CHANNELS = 'CHAT_CREATE_CHANNELS' in os.environ
# Configuration constants
HELP_MSG = '''Welcome to the social channel!
Please use $play to chat with some complete strangers. It will be a lot of fun!
'''
def are_we_allowed_to_chat(ctx, channel_id=CHANNEL_ID):
"""Return True if we are allowed to chat in a given channel."""
return channel_id is None or channel_id == ctx.channel.id
class ChatCog(commands.Cog):
def __init__(self, bot,
channel_id=CHANNEL_ID,
voice_cat=VOICE_CHANNEL_CAT,
help_msg=HELP_MSG,
help_msg_delay=HELP_MSG_DELAY):
self.bot = bot
self.channel_id = channel_id
self.voice_cat = None
self.voice_cat_name = voice_cat
self.help_msg = help_msg
self.help_msg_every = help_msg_delay
self._waiting = set()
self._chatting = set()
self._last_help_msg = None
self._voice_chs = []
async def get_voice_channels(self, guild):
"""
Get all voice channels under the `self.voice_cat_name` category.
Retrun
List[VoiceChannel]: if the category exists
None: if the category does not exist.
"""
# Get the list of voice channels and all folks in them.
for cat, chs in guild.by_category():
if cat.name == self.voice_cat_name:
self.voice_cat = cat
return chs
return
async def create_voice_channel(self, guild):
return await guild.create_voice_channel(
str(uuid.uuid1()),
category=self.voice_cat,
user_limit=NUM_PARTICIPANTS
)
@commands.Cog.listener()
async def on_ready(self):
"""
Called automatically when the bot receves a 'ready' event. It can be
called more than once!
"""
await self.bot.change_presence(
status=Status.online,
activity=Activity(type=ActivityType.listening, name='$help')
)
print('Bot is ready')
@commands.command(help='Chat with random people from the conference')
async def play(self, ctx, *, member: Member = None):
"""
Main idea: create a number of voice chat channels under the same
category. Limit the number of participants per channel to
NUM_PARTICIPANTS. As soon as people invoke this command, put them in
the first channel that has a slot available. If no channels with any
free slot are available, create a new one.
Ideally the channels are invite only.
"""
member = member or ctx.author
self._voice_chs = await self.get_voice_channels(ctx.guild)
if self._voice_chs is None:
# The voice channel category does not exist!
print(f'Please (re)create the {self.voice_cat_name} category')
await member.send(f'Hello {member.name}, we are having problems ' +
'with the social chat. Please contact @loc')
return
available = []
for ch, members in [(ch, ch.members) for ch in self._voice_chs]:
if len(members) < NUM_PARTICIPANTS:
available.append(ch)
if member in members:
await member.send(
f'Hello {member.name}, it looks like you are already ' +
f'chatting in channel {ch.name}.'
)
return
if not available and CAN_CREATE_CHANNELS:
# Need to create more channels!
try:
ch = await self.create_voice_channel(ctx.guild)
except Exception as e:
print(f'Unable to create new voice channel: {e!r}')
await member.send('All channels are full. ' +
'Please try again later')
return
else:
self._voice_chs.append(ch)
elif available:
ch = available[0]
else:
await member.send('All channels are full. Please try again later')
return
invite = await ch.create_invite(max_uses=1)
await member.send(f'Hello {member.name}, follow this invite to ' +
'start chatting: ' + invite.url)
async def resend_help(self):
await self.bot.wait_until_ready()
ch = self.bot.get_channel(self.channel_id)
while True:
messages = await ch.history(after=self._last_help_msg).flatten()
if len(messages) >= self.help_msg_every:
self._last_help_msg = await ch.send(self.help_msg)
await asyncio.sleep(self.help_msg_every)
if __name__ == '__main__':
bot = commands.Bot(command_prefix='$')
bot.add_check(are_we_allowed_to_chat, call_once=False)
cog = ChatCog(bot, channel_id=CHANNEL_ID)
bot.add_cog(cog)
bot.loop.create_task(cog.resend_help())
bot.run(BOT_TOKEN)