]> code.communitydata.science - coldcallbot-discord.git/blob - coldcallbot.py
I added a network game to the bot. Need to refactor it but it's working for now
[coldcallbot-discord.git] / coldcallbot.py
1 #!/usr/bin/env python3
2
3 from coldcall import ColdCall
4 import re
5 import discord
6 import config
7 import random
8
9 ## create the coldcall object
10 cc = ColdCall()
11
12 class ColdCallBot (discord.Client):
13     async def on_ready(self):
14         print(f'Logged on as {self.user}! Ready for class!')
15
16     async def on_message(self, message, voice_channel = 'Class Sessions'):
17         if message.author == self.user:
18             return
19
20         if message.content.startswith('$next'):
21             if message.channel.category:
22                 if cc.course != message.channel.category:
23                     cc.update_course(message.channel.category)
24             classroom = [x for x in message.guild.voice_channels if x.name == voice_channel and x.category_id == message.channel.category_id][0]
25
26             present_students = []
27             for member in classroom.members:
28                 if 'Students' in [r.name for r in member.roles]:
29                     present_students.append(re.sub(r'^(.*)\#.*$', r'\1', member.name))
30
31             # print who is online
32             print(f'currently online: {",".join(present_students)}')
33
34             if len(present_students) < 1:
35                 msg_text = "I don't see any students currently in the Classroom Voice channel!"
36             else:
37                 msg_text = cc.coldcall(present_students)
38
39             await message.channel.send(msg_text)
40         # TODO: Only let admin send this command
41         if (message.content.startswith('$network game')) and ('Teachers' in [r.name for r in message.author.roles]):
42             print("Starting the game")
43             if message.channel.category:
44                 if cc.course != message.channel.category:
45                     cc.update_course(message.channel.category)
46             classroom = [x for x in message.guild.voice_channels if x.name == voice_channel and x.category_id == message.channel.category_id][0]
47
48             present_students = []
49             for member in classroom.members:
50                 if 'Students' in [r.name for r in member.roles]:
51                     present_students.append(member)
52
53             self.assignments = get_assignments(present_students, edgelist = './network_game/test_edgelist.csv')
54             # Build a mapping from names to user objects so that people can refer to users
55             self.active_list = {x.name: x for x in self.assignments}
56             self.observers = [x for x in classroom.members if x not in self.assignments]
57             if self.assignments is not None:
58                 for student in self.assignments:
59                     await student.send(f"You are allowed to talk to:")
60                     for neighbor in self.assignments[student]['neighbors']:
61                         await student.send(f"{neighbor.mention}")
62                     await student.send(f"You have these resources: {self.assignments[student]['has']}.")
63                     await student.send(f"You need: {self.assignments[student]['needs']}.")
64             else:
65                 for student in present_students:
66                     await student.send("Not enough students to play")
67
68         if message.content.startswith('$send'):
69             try:
70                 _, resource, u_to = message.content.split(' ')
71             except:
72                 await message.author.send("Badly formed command. It has to be '$send resource @user'")
73             if u_to not in self.active_list:
74                 await message.author.send(f"I can't find {u_to} in the list of users. Make sure the command is formatted as $send resource @user")
75             else:
76                 u_to = self.active_list[u_to]
77                 gave_resource = self.give_resource(resource, message.author, u_to)
78                 if gave_resource == True:
79                     finished = self.is_finished(u_to)
80                     await message.author.send(f"{resource} sent to {u_to}")
81                     await message.author.send(f"You now have {self.assignments[message.author]['has']} and you need {self.assignments[message.author]['needs']}")
82                     if finished:
83                         await u_to.send("You have everything you need! Well done! You can keep passing resources and talking with your 'neighbors' if you like. Just make sure to keep the resources that you need!")
84                     else:
85                         await u_to.send(f"You now have {self.assignments[u_to]['has']} and you need {self.assignments[u_to]['needs']}")
86
87                     for o in self.observers:
88                         await o.send(f"{message.author.name} sent {resource} to {u_to.name}")
89                         if finished:
90                             await o.send(f"{u_to.name} has everything they need!!!")
91
92                 else:
93                     await message.author.send(f"Resource not sent. Are you sure you have {resource}?")
94     def is_finished(self, u_to):
95         if set(self.assignments[u_to]['needs']) <= set(self.assignments[u_to]['has']):
96             return True
97         return False
98
99
100
101
102
103
104     def give_resource(self, resource, u_from, u_to):
105         if resource not in self.assignments[u_from]['has']:
106             return False
107         else:
108             self.assignments[u_from]['has'].remove(resource)
109             self.assignments[u_to]['has'].append(resource)
110             return True
111
112
113 def get_assignments(student_list,
114         edgelist = './network_game/edgelist.csv',
115         resource_prefix = './network_game/resources_'
116         ):
117
118
119     def _add_connection(node1, node2):
120         node1 = int(node1)
121         node2 = int(node2)
122         for i in range(len(mapping[node1])):
123             s1 = mapping[node1][i]
124             s2 = mapping[node2][i]
125             if s1 in assignments:
126                 assignments[s1]['neighbors'].append(s2)
127             else:
128                 assignments[s1] = {'neighbors': [s2]}
129
130     def _add_resources():
131         fn = f"{resource_prefix}{group_size}.csv"
132         with open(fn, 'r') as f:
133             i = 1
134             for line in f.readlines():
135                 resources = line.strip().split(',')
136                 curr_students = mapping[i]
137                 for s in curr_students:
138                     assignments[s]['has'] = resources[:3]
139                     assignments[s]['needs'] = resources[3:]
140                 i += 1
141
142
143
144     assignments = {}
145     group_size = _get_group_size(len(student_list))
146     if len(student_list) < group_size:
147         return None
148     mapping = _make_mapping(student_list, group_size)
149     with open(edgelist, 'r') as f:
150         for line in f.readlines():
151             node1, node2 = line.strip().split(',')
152             if int(node2) <= group_size:
153                 _add_connection(node1, node2)
154                 _add_connection(node2, node1)
155     _add_resources()
156     return assignments
157
158
159
160 def _make_mapping(students, group_size):
161     random.shuffle(students)
162     n_observers = len(students) % group_size
163     mapping = {}
164     if n_observers > 0:
165         mapping['observers'] = students[-n_observers:]
166     for i, student in enumerate(students[-n_observers:]):
167         j = i % group_size
168         idx = j + 1
169         if idx in mapping:
170             mapping[idx].append(student)
171         else:
172             mapping[idx] = [student]
173     return mapping
174
175
176 def _get_group_size(n):
177     min_observers = None
178     for x in range(7,10):
179         observers = n % x
180         if min_observers is None or observers < min_observers:
181             best_fit = x
182             min_observers = observers
183     return best_fit
184
185
186
187
188
189 # this is necessary to get information about who is online
190 intents = discord.Intents.default()
191 intents.members = True
192 intents.presences = True
193
194 ccb = ColdCallBot(intents=intents)
195 ccb.run(config.key)

Community Data Science Collective || Want to submit a patch?