-
Notifications
You must be signed in to change notification settings - Fork 6
/
bot.py
688 lines (593 loc) · 35.4 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
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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
"""OSR Discord Bot."""
import os
import discord
from discord.ext import commands
from datetime import datetime
import asyncio
import requests
import aiohttp
import re
import kgs
from bs4 import BeautifulSoup
from difflib import SequenceMatcher
from typing import Dict, List, Tuple # noqa
import math
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from config import roles_dict, guild_id, prefix, channels
from utils import add_footer, add_role, get_user
from utils import user_info_message, user_info_embed, user_rank
bot = commands.Bot(command_prefix=prefix)
roles_are_set = False
kgs_to_send = []
SPECIAL_MESSAGES = {} # type: Dict[int, SpecialMessage]
class UnsentMessage():
def __init__(self, message: str, embed: discord.Embed) -> None:
self.message = message
self.embed = embed
async def send(self, ctx: commands.Context) -> None:
await ctx.send(self.message, embed=self.embed)
class SpecialMessage():
def __init__(self, message: discord.Message, originator: discord.User) -> None:
self.message = message
self.originator = originator
async def on_reaction_add(self, reaction: discord.Reaction, user: discord.User) -> None:
pass
class WhoMessage(SpecialMessage):
def __init__(self, message: discord.Message, originator: discord.User, matches: List[discord.User]) -> None:
super().__init__(message, originator)
self.matches = matches
async def on_reaction_add(self, reaction: discord.Reaction, user: discord.User) -> None:
if user != self.originator:
return
try:
idx = int(reaction.emoji[0]) - 1
info = get_user_info(self.matches[idx])
add_footer(info.embed, self.originator)
await self.message.clear_reactions()
# This needs to be here for some reason, else we get an extra name after edit...
# no more since discord.py 1.2.5
#await self.message.edit(embed=None)
await self.message.edit(content=info.message, embed=info.embed)
del SPECIAL_MESSAGES[self.message.id]
except (ValueError, IndexError):
# Wrong emoji
return
async def get_roles():
global roles_are_set
if not roles_are_set:
for name, role_dict in roles_dict.items():
role = discord.utils.get(bot.get_guild(guild_id).roles, id=role_dict['id'])
roles_dict[name].update({"role": role})
roles_are_set = True
# When a new member joins, tag them in "welcome" channel and let them know of our bot
@bot.event
async def on_member_join(member):
"""On member join, display welcome message and add the player role."""
if bot.user.name != "OSR Bot":
welcome_ch = bot.get_channel(channels["welcome"])
general_ch = bot.get_channel(channels["general"])
bot_commands_ch = bot.get_channel(channels["bot-commands"])
msg = """Welcome to OSR {member}! We are delighted to have you with us.\n
I am here to assist you. You can either send me a private message or invoke my commands in the correct channels.
Try, for example, to send `!help` to me, or type it in {bot_commands} to see what I can do for you.
Otherwise, simply introduce yourself in {general} or talk to any of our team members.\n
You have been assigned the "player" role meaning you are interested in playing go games.
If you don't like that, you can always remove yourself the role byt typing `!no go`.\n
Finally, you can find out more about Open Study Room at https://openstudyroom.org/
We hope that you enjoy your time with us! : )""".format(member=member.mention,
bot_commands=bot_commands_ch.mention,
general=general_ch.mention)
await welcome_ch.send(msg)
role_dict = roles_dict['go']
role = role_dict["role"]
await member.add_roles(role)
@bot.event
async def on_message(message):
ctx = await bot.get_context(message)
if ctx.command is None:
# Not a valid command (Normal message or invalid command)
# If it's in general channel we add it in the kgs_to_send queue
if message.channel == bot.get_channel(channels["general"]) and ctx.author != bot.user:
text = str(ctx.author.display_name) + ": " + message.content
kgs_to_send.append(text)
return
await bot.process_commands(message)
# Here are the pictures commands. That's just for fun.
PICTURE_COMMANDS = {
"cho": "https://cdn.discordapp.com/attachments/456532168370290695/461802038276390923/cho.png",
"cho_hug": "https://cdn.discordapp.com/attachments/430062036903395329/444192620504416268/WroCzKKKj7o.png",
"chang_ho": "https://cdn.discordapp.com/attachments/430062036903395329/432619582054858806/153746110828-nong01.png",
"yuta": "https://cdn.discordapp.com/attachments/287487891003932672/461811731359072259/vfcp43js2.png",
"kj_facepalm": "https://cdn.discordapp.com/attachments/366870031285616651/461813881900236821/iozlnkjg.png",
"scary": "https://cdn.discordapp.com/attachments/463639475751354368/467077666298789908/Head.png"
}
@bot.event
async def on_reaction_add(reaction: discord.Reaction, user: discord.User) -> None:
if reaction.message.id in SPECIAL_MESSAGES:
await SPECIAL_MESSAGES[reaction.message.id].on_reaction_add(reaction, user)
def picture_command(url):
async def inner(ctx):
embed = discord.Embed(description="Requested by: " + ctx.author.mention)
embed.set_thumbnail(url=ctx.author.avatar_url)
embed.set_image(url=url)
await ctx.send(embed=embed)
return inner
for name, url in PICTURE_COMMANDS.items():
bot.command(pass_context=True, name=name)(picture_command(url))
# Roles managment start here
def role_command(role):
async def inner(ctx):
await add_role(ctx, role)
return inner
for role, role_dict in roles_dict.items():
bot.command(pass_context=True, name=role)(role_command(role))
@bot.command(pass_context=True)
async def no(ctx, role_name):
role_dict = roles_dict.get(role_name)
if role_dict is None:
return
if str(ctx.message.channel) not in role_dict['allowed_channels']:
message = "Please " + ctx.message.author.mention + ", use the appropriate channels for this command: "
message += ', '.join(role_dict['allowed_channels'])
await ctx.send(message)
return
role = role_dict["role"]
await ctx.message.author.remove_roles(role)
await ctx.send(ctx.message.author.mention + " is no longer " + role_dict["verbose"] + ".")
def get_user_info(user: discord.User) -> UnsentMessage:
infos = requests.get("https://openstudyroom.org/league/discord-api/", params={'uids': [user.id]}).json()
if not infos:
message = ('{} was too lazy to link their OSR account with their discord. '
'They just have to follow this [link](https://openstudyroom.org/discord/)!').format(user.mention)
embed = discord.Embed(title="Lazy " + user.name, description=message, color=0xeee657)
return UnsentMessage("", embed)
else:
embed = user_info_embed(user, infos)
return UnsentMessage("", embed)
@bot.command(pass_context=True)
async def rank(ctx: commands.Context, username: str = None) -> None:
"""Show rank graphs for OGS and KGS servers."""
if username is None:
last_message = await ctx.message.channel.history(limit=1).flatten()
user = last_message[0].author
else:
user = get_user(username, bot)
if user is not None:
infos = requests.get("https://openstudyroom.org/league/discord-api/", params={'uids': [user.id]}).json()
info = infos.get(str(user.id))
if info is not None:
kgs_username = info.get('kgs_username')
ogs_username = info.get('ogs_username')
ogs_id = info.get('ogs_id')
if kgs_username is not None:
embed = discord.Embed(title="KGS rank history for " + str(username), color=0xeee657)
embed.set_image(url="http://www.gokgs.com/servlet/graph/"+kgs_username+"-en_US.png")
add_footer(embed, ctx.author)
await ctx.send(embed=embed)
if ogs_username is not None:
def format_gorank(value):
if value == 0:
return "1d"
elif value > 0:
return str(int(value))+"k"
elif value < 0:
return str(1+abs(int(value)))+"d"
r = requests.get('https://online-go.com/termination-api/player/'+str(ogs_id)+'/rating-history?speed=overall&size=0')
rl = r.text.split('\n')
rank = []
dates = []
for game in range(1, len(rl)-1):
rank.append(30-(31.25)*math.log(float(rl[game].split('\t')[4]) / 850))
dates.append(datetime.utcfromtimestamp(int(rl[game].split('\t')[0])).strftime('%d/%m/%Y'))
x = [datetime.strptime(d, '%d/%m/%Y').date() for d in dates]
y = range(len(x))
fig, ax = plt.subplots(nrows=1, ncols=1)
plt.plot(x, rank, color=(0, 194/255, 0))
ax.xaxis.set_major_formatter(mdates.DateFormatter('\n%Y'))
ax.xaxis.set_major_locator(mdates.YearLocator())
ax.invert_yaxis()
fig.canvas.draw()
labels = [format_gorank(float(item.get_text().replace('−', '-'))) for item in ax.get_yticklabels()]
ax.set_yticklabels(labels)
fig.patch.set_facecolor((236/255, 236/255, 176/255))
ax.patch.set_facecolor('black')
ax.yaxis.grid(linewidth=0.2)
plt.title("OGS Rank history for " + ogs_username)
fig.savefig('Rank.png', bbox_inches='tight')
file = discord.File('Rank.png', filename="OGS Rank history for " + ogs_username +".png")
await ctx.send(file=file)
os.remove('Rank.png')
return
# Look for nearest matches, if they exist
users = bot.get_guild(guild_id).members # type: List[discord.Member]
# Just using sequencematcher because its simple and no need to install extra Library
# If keen on better distrance metrics, look at installing Jellyfish or Fuzzy Wuzzy
similarities = [(member,
max(SequenceMatcher(None, username.lower(), member.display_name.lower()).ratio(),
SequenceMatcher(None, username.lower(), member.name.lower()).ratio())) for member in users]
similarities.sort(key=lambda tup: tup[1], reverse=True)
# unlikely to get 5 with >70% match anyway...
top_matches = [x for x in similarities[:5] if x[1] > 0.7] # type: List[Tuple[discord.Member, float]]
uids = [x[0].id for x in top_matches]
infos = requests.get("https://openstudyroom.org/league/discord-api/", params={'uids': uids}).json()
# Split and recombine so that OSR members appear top of list
osr_members = [x for x in top_matches if infos.get(str(x[0].id)) is not None]
not_osr_members = [x for x in top_matches if x not in osr_members]
top_matches = osr_members + not_osr_members
message = ''
for _i, x in enumerate(top_matches):
message += '\n{}\N{COMBINING ENCLOSING KEYCAP}**{}**#{} {}'.format(_i + 1,
x[0].display_name,
x[0].discriminator,
user_rank(x[0], infos))
if username in roles_dict:
message += "\n\n However, `" + username + "` is a valid role. Did you mean `!list " + username + "`?"
nearest_or_sorry = '", nearest matches:' if top_matches else '", sorry'
embed = discord.Embed(description=message, title='No users by the exact name "' + username + nearest_or_sorry)
add_footer(embed, ctx.message.author)
msg = await ctx.send(embed=embed)
@bot.command(pass_context=True, aliases=['user'])
async def who(ctx: commands.Context, username: str = None) -> None:
if username is None:
last_message = await ctx.message.channel.history(limit=4).flatten()
user = next(u.author for u in last_message[1:] if u.author.id != 461792018843172866)
elif username == "me":
last_message = await ctx.message.channel.history(limit=1).flatten()
user = last_message[0].author
else:
user = get_user(username, bot)
# If the message contains a mention, use that
if user is None and ctx.message.mentions:
user = ctx.message.mentions[0]
if user is not None:
info = get_user_info(user)
add_footer(info.embed, ctx.message.author)
await info.send(ctx)
return
# Look for nearest matches, if they exist
users = bot.get_guild(guild_id).members # type: List[discord.Member]
# Just using sequencematcher because its simple and no need to install extra Library
# If keen on better distrance metrics, look at installing Jellyfish or Fuzzy Wuzzy
similarities = [(member,
max(SequenceMatcher(None, username.lower(), member.display_name.lower()).ratio(),
SequenceMatcher(None, username.lower(), member.name.lower()).ratio())) for member in users]
similarities.sort(key=lambda tup: tup[1], reverse=True)
# unlikely to get 5 with >70% match anyway...
top_matches = [x for x in similarities[:5] if x[1] > 0.7] # type: List[Tuple[discord.Member, float]]
uids = [x[0].id for x in top_matches]
infos = requests.get("https://openstudyroom.org/league/discord-api/", params={'uids': uids}).json()
# Split and recombine so that OSR members appear top of list
osr_members = [x for x in top_matches if infos.get(str(x[0].id)) is not None]
not_osr_members = [x for x in top_matches if x not in osr_members]
top_matches = osr_members + not_osr_members
message = ''
for _i, x in enumerate(top_matches):
message += '\n{}\N{COMBINING ENCLOSING KEYCAP}**{}**#{} {}'.format(_i + 1,
x[0].display_name,
x[0].discriminator,
user_rank(x[0], infos))
if username in roles_dict:
message += "\n\n However, `" + username + "` is a valid role. Did you mean `!list " + username + "`?"
nearest_or_sorry = '", nearest matches:' if top_matches else '", sorry'
embed = discord.Embed(description=message, title='No users by the exact name "' + username + nearest_or_sorry)
footer_text = "{}#{} can react to this message to display one user infos".format(ctx.message.author.name, ctx.message.author.discriminator)
embed.set_footer(text=footer_text, icon_url=ctx.message.author.avatar_url)
msg = await ctx.send(embed=embed)
for _i, match in enumerate(top_matches): # type: Tuple[int, Tuple[discord.Member, float]]
await msg.add_reaction(str(_i + 1) + '\N{COMBINING ENCLOSING KEYCAP}')
SPECIAL_MESSAGES[msg.id] = WhoMessage(msg, ctx.message.author, [x[0] for x in top_matches])
@bot.command(pass_context=True, aliases=["whos"])
async def list(ctx, role_name):
role_dict = roles_dict.get(role_name)
if role_dict is None:
return
if str(ctx.message.channel) not in role_dict['allowed_channels']:
message = "Please " + ctx.message.author.mention + ", use the appropriate channels for this command: "
message += ' '.join(role_dict['allowed_channels'])
await ctx.send(message)
return
role = role_dict["role"]
online_users = [x for x in role.members if str(x.status) == "online"]
idle_users = [y for y in role.members if str(y.status) == "idle"]
users = online_users
if len(users) > 0:
if len(users) > 15:
message = "Sorry, but there are too many users in the " + role_name + " group to list."
await ctx.send(message)
return
uids = [member.id for member in users]
infos = requests.get("https://openstudyroom.org/league/discord-api/", params={'uids': uids}).json()
message = ''
message2 = ''
for user in users:
new_user_message = user_info_message(user, infos)
if len(message) + len(new_user_message) < 2048:
message += new_user_message
else:
message2 += new_user_message
if message2 == '':
message += "*" + str(len(idle_users)) + " more users are idle.*"
else:
message2 += "*" + str(len(idle_users)) + " more users are idle.*"
title = "The following users are " + role_dict['verbose'] + ":"
embed = discord.Embed(title=title, description=message)
add_footer(embed, ctx.message.author)
await ctx.send(embed=embed)
if message2 != '':
embed = discord.Embed(description=message2)
add_footer(embed, ctx.message.author)
await ctx.send(embed=embed)
else:
await ctx.send("Sorry {}. Unfortunately, nobody is {} right now. :(".format(ctx.message.author.mention,
role_dict['verbose']))
@bot.command(pass_context=True)
async def info(ctx):
desc = "Help manage the OSR discord.\nhttps://github.com/climu/OSR-discord-bot"
embed = discord.Embed(title="OSR bot", description=desc, color=0xeee657)
add_footer(embed, ctx.author)
await ctx.send(embed=embed)
bot.remove_command('help')
@bot.command(pass_context=True)
async def help(ctx, subject=None):
if subject is None:
desc = "Help organise this discord channel. The following commands are available:"
embed = discord.Embed(title="OSR Bot", description=desc, color=0xeee657)
embed.add_field(name="**!roles**",
value="Display help file regarding the Discord OSR roles system.",
inline=False)
embed.add_field(name="**!who [username or #discriminator]**",
value=("Get one user info: will give informations about a user given his nickname or "
"discriminator. For instance, "
"my discriminator is `#{}`.").format(bot.user.discriminator),
inline=False)
embed.add_field(name="**!league**", value="Find out about OSR leagues.", inline=False)
embed.add_field(name="**!sensei [term]**",
value="Display information for a term from Sensei's Library.",
inline=False)
embed.add_field(name="**!rank**", value="Get KGS rank history for a specific user.", inline=False)
embed.add_field(name="**!info**", value="Gives a little info about the bot.", inline=False)
embed.add_field(name="**!help**", value="Gives this message.", inline=False)
embed.add_field(name="**!help osr**",
value="Find out how you can help with our community.",
inline=False)
add_footer(embed, ctx.author)
await ctx.send(embed=embed)
else:
if subject == "osr":
title = "I like this project. How can I help?"
message = ("There are many ways you can help the OSR project if you like to. "
"Those include but are not limited to:\n" +
" - Playing in our leagues.\n" +
" - Keeping OSR friendly and active.\n" +
" - Giving a couple of $/€ so we can pay for the server and set up quality teaching.\n" +
" - Help us run the community.\n" +
"You can find more details about that [here](https://openstudyroom.org/help-osr/).")
embed = discord.Embed(title=title, description=message, color=0xeee657)
add_footer(embed, ctx.author)
await ctx.send(embed=embed)
@bot.command(pass_context=True)
async def roles(ctx):
desc = "Help organise the discord channel by self-assigning various roles."
embed = discord.Embed(title="Roles system", description=desc, color=0xeee657)
value = "To avoid using `@here`, users can choose to be in groups of interest:\n\n"
value += "- **!go**: will assign you the `@player` role. This is for people who are interested in playing OSR games. Tag `@player` when you are looking for a game.\n\n"
value += "- **!nine**: will assign you the `@9x9` role for playing 9x9 games, similar to the `@player` role.\n\n"
value += "- **!tsumego**: will assign you the `@tsumegoer` role. This is for people who are interested in tsumego study. Tag `@tsumegoer` when you post a new tsumego or have a related question.\n\n"
value += "- **!review**: will assign you the `@reviewer` role. This is for people who are available to give game reviews. Tag `@reviewer` to ask for a game review.\n\n"
value += "- **!dan/sdk/ddk**: will assign you the `@dan`, `@sdk` or `@ddk` role. By saying your approximate level, it will allow users to tag the appropriate group when lookinSg for game or help. Feel free to sign up to more than one groups.\n\n"
value += "- **!goquest**: will assign you the `@GoQuest` role for playing games on that server.\n\n"
embed.add_field(name="Add a role", value=value, inline=False)
embed.add_field(name="Remove a role", value="**!no [role]**: will remove the role. For instance, `!no go` will remove you from the `@player` role", inline=False)
embed.add_field(name="List all online users in with a specific role", value="**!list [role]**: will list all online users with the said role. For instance `!list tsumego` will list all online users of the `@tsumego` role.", inline=False)
add_footer(embed, ctx.author)
await ctx.send(embed=embed)
@bot.command(pass_context=True)
async def league(ctx, subject=None):
if subject is None:
desc = "Information about the various leagues hosted by OpenStudyRoom."
embed = discord.Embed(title="OpenStudyRoom Leagues", description=desc, color=0xeee657)
value = "The OpenStudyRoom hosts four different leagues, more information of which you can find by appending the following to `!league #`:\n\n"
value += "- **ladder**: The main OSR league, also called the **OSR monthly league**.\n\n"
value += "- **meijin**: Encompasing all other leagues and games.\n\n"
value += "- **ddk**: A league dedicated to double digit kyu players.\n\n"
value += "- **dan**: A league dedicated to dan players.\n\n"
embed.add_field(name="Overview", value=value, inline=False)
value = "Further information can be found with the following commands:\n\n"
embed.add_field(name="Other commands", value=value, inline=False)
embed.add_field(name="**rules**", value="Global league rules", inline=True)
embed.add_field(name="**join**", value="Joining our leagues", inline=True)
embed.add_field(name="**faq**", value="Frequently asked questions", inline=True)
add_footer(embed, ctx.author)
await ctx.send(embed=embed)
elif subject == "rules":
desc = "The following rules apply to all league games. Individual leagues may have their own rules."
embed = discord.Embed(title="League rules", description=desc, color=0xeee657, url="https://openstudyroom.org/league/league-rules/")
value = "The following rules apply in all our leagues:\n\n"
value += "- Games must be played on the [KGS go server](https://www.gokgs.com/) or on the [Online Go Server](https://online-go.com/).\n"
value += "- One player needs to **type `#OSR`** in the game chat for that game to count.\n"
value += "- A game can count for both leagues, just type both tags in the game chat.\n"
value += "- Games must be played with no handicap.\n"
value += "- **Komi** must be **6.5**.\n"
value += "- Game must use **Japanese time setting** with at least **30 minutes main time** and **5x30 seconds byo-yomi**.\n"
value += "- Game must be **public**.\n"
value += "- The game can be ranked or unranked, to the convenience of the players.\n"
value += "- Using a bot such as Leela or Crazy Stone during league game is strictly forbidden. The OSR team checks games from time to time to look for suspicious matching with these go engines.\n\n\n"
embed.add_field(name="Globar league rules", value=value, inline=False)
value = "Players are strongly encouraged to review the game afterwards. If the players have similar rank, or if your opponent can't help you reviewing your game, one should feel free to ask stronger players in our Discord channel and/or in our KGS room to help with the review.\n\n"
value += "Should you have any questions, or you've played a game that doesn't strictly cohere with all the rules, feel free to ask a member of our team for help.\n"
embed.add_field(name="Additional information", value=value, inline=False)
add_footer(embed, ctx.author)
await ctx.send(embed=embed)
elif subject == "ladder" or subject == "monthly":
desc = "The following rules apply to the OSR Ladder.\n\n"
desc += "- Players can play up to 3 games against the same opponent within the group.\n\n"
desc += "- A win grants 1.5 points and a loss grants 0.5.\n\n"
desc += "- Players who played at least 1 games will be added to the next league, once this one has ended.\n\n"
embed = discord.Embed(title="OSR Ladder (Monthly league)",
description=desc,
color=0xeee657,
url="https://openstudyroom.org/league/ladder/")
add_footer(embed, ctx.author)
await ctx.send(embed=embed)
elif subject == "meijin":
desc = "The following rules apply to the Meijin league.\n\n"
desc += "- Players can play up to 5 games against the same opponent within the group.\n\n"
desc += "- A win grants 1.5 points and a loss grants 0.5.\n\n"
desc += "- Players who played at least 3 games will be automatically added to the next league, once this one has ended.\n\n"
embed = discord.Embed(title="Meijin League",
description=desc,
color=0xeee657,
url="https://openstudyroom.org/league/meijin/")
add_footer(embed, ctx.author)
await ctx.send(embed=embed)
elif subject == "ddk":
desc = "The following rules apply to the DDK league.\n\n"
desc += "- Players can play up to 3 games against the same opponent within the group.\n\n"
desc += "- A win grants 1.5 points and a loss grants 0.5.\n\n"
desc += "- Players who played at least 1 games will be added to the next league, once this one has ended.\n\n"
embed = discord.Embed(title="DDK League", description=desc, color=0xeee657, url="https://openstudyroom.org/league/ddk/")
add_footer(embed, ctx.author)
await ctx.send(embed=embed)
elif subject == "dan":
desc = "The following rules apply to the Dan league.\n\n"
desc += "- Players can play up to 3 games against the same opponent within the group.\n\n"
desc += "- A win grants 1.5 points and a loss grants 0.5.\n\n"
desc += "- Players who played at least 1 games will be added to the next league, once this one has ended.\n\n"
embed = discord.Embed(title="Dan League", description=desc, color=0xeee657, url="https://openstudyroom.org/league/dan/")
add_footer(embed, ctx.author)
await ctx.send(embed=embed)
elif subject == "join":
desc = "With the exception of the **Meijin League**, you need to manually join the league you wish.\n\n"
desc += "In order to do this, go to our [website](https://openstudyroom.org/league/) and select the league of your choice.\n\n"
desc += "In the main page of the league, you should see at the right of the top banner a button to join the specific league.\n\n"
desc += "Press on that, and you have joined!\n\n"
desc += "An example of this is presented in the image below for the **Dan League**.\n\n"
embed = discord.Embed(title="Joining any of the OSR Leagues", description=desc, color=0xeee657)
embed.set_image(url="https://cdn.discordapp.com/attachments/464175979406032897/464909106520522764/join_osr_league_annotated.png")
add_footer(embed, ctx.author)
await ctx.send(embed=embed)
elif subject == "faq":
desc = "You can always ask one of our team members, but here is a small FAQ."
embed = discord.Embed(title="Frequently Asked Questions", description=desc, color=0xeee657)
question = "We forgot to tag `#osr` the game, what can we do?"
answer = "Contact one of the team members to see if we can manually add your game."
embed.add_field(name=question, value=answer, inline=False)
question = "Our game was classified as private, can we still use it for the league?"
answer = "You need to upload the SGF file in our Discord and notify one of the members to manually add your game."
embed.add_field(name=question, value=answer, inline=False)
question = "At the end of the month, can I win a prize as a league player?"
answer = ("Yes, it is possible to win a prize as a league player. Thanks to the support of our teachers, friends and partners, we are happy to reward our winners with the folowing:\n" +
" - Teaching game with [Péter Markó](https://openstudyroom.org/teachers/marko-peter/) (4 dan EGF).\n" +
" - Teaching game with with [Alexandre Dinerchtein](http://breakfast.go4go.net/) (3 dan pro).\n" +
" - Game commentary with [Justin Teng](https://openstudyroom.org/teachers/justin-teng/) (AGA 6 dan).\n" +
" - 5€ gift certificate at [Guo Juan's internet go school](https://internetgoschool.com/).\n\n")
embed.add_field(name=question, value=answer, inline=False)
question = "I am having some issues with another OSR member, what can I do?"
answer = ("In the unfortunate event that you have an issue with" +
" another member from OSR, we ask you that you contact" +
" one of our team members privately so we can address the " +
" issue.\n\n")
answer += ("Please note that we do not want to insult anyone and the" +
" any problem will be dealt with discretly.")
embed.add_field(name=question, value=answer, inline=False)
add_footer(embed, ctx.author)
await ctx.send(embed=embed)
else:
desc = "I am not currently programmed for the command: **" + subject + "**"
embed = discord.Embed(title="Unknown command", description=desc, color=0xeee657)
embed.set_thumbnail(url="https://cdn.discordapp.com/attachments/464175979406032897/464915353382813698/error.png")
add_footer(embed, ctx.author)
await ctx.send(embed=embed)
@bot.command(pass_context=True, aliases=["Quote"])
async def quote(ctx, msg_id, *resp):
"""Take message ID and response and create a quote."""
str_resp = ' '.join(resp)
"""Take a message ID as input and convert it to an embed with quoted text."""
message = await ctx.get_message(msg_id)
await ctx.send(message.author.mention)
embed = discord.Embed(description=str_resp, color=0x63b6f3)
embed.set_author(name=ctx.author.name + " replied:", icon_url=ctx.author.avatar_url)
embed.set_thumbnail(url='https://www.shareicon.net/data/64x64/2016/07/10/119195_chat_512x512.png')
embed.add_field(name="In response to:",
value=message.author.mention + ": " + message.content,
inline=True)
await ctx.send(embed=embed)
@bot.command(pass_context=True, aliases=["define"])
async def sensei(ctx, term=None):
"""Get information from Sensei's Library."""
if term is None:
message = "To search in Sensei's Library, please add a term as: !sensei term"
embed = discord.Embed(title="Please add a search term",
description=message, color=0xeee657)
embed.set_thumbnail(url="https://senseis.xmp.net/images/stone-hello.png")
add_footer(embed, ctx.author)
await ctx.send(embed=embed)
else:
s = requests.Session()
url = "https://senseis.xmp.net/"
s.headers.update({'referer': url})
params = {'searchtype': 'title',
'search': term
}
# Get all results searching by title
r = s.get(url, params=params)
# Separate direct hit
regex = (r"\<b\>Direct hit\:\<br\>\<a href=\"\/\?(?P<term_url>.*?)\"" +
r"\>(?P<term>.*?)\<\/a\>\<\/b\>")
match = re.search(regex, r.text, re.IGNORECASE)
if match:
url = "https://senseis.xmp.net/?" + match.group('term_url')
response = requests.get(url)
soup = BeautifulSoup(response.text, "html.parser")
paragraphs = soup.find_all("p")
title = "**" + soup.title.string + "**"
message = paragraphs[1].text + "\n"
message += "[See more online]({}) on Sensei's Library.".format(url)
else:
title = "**The term '{}' was not found**".format(term)
message = ("The exact term {} was not found on".format(term) +
" Sensei's Library.")
embed = discord.Embed(title=title, description=message, color=0xeee657)
embed.set_thumbnail(url="https://senseis.xmp.net/images/stone-hello.png")
# Search for non-direct hits containing the words
regex = (r"\<b\>Title containing word( starting with search term)?" +
r"\:\<\/b\>\<br\>\n(?:<img .*?)?(?:<a href=\"" +
r"/\?(.*?)\">(.*?)</a>.*?\n){1,5}")
match = re.search(regex, r.text, re.MULTILINE)
# If there are alternatives, add them in the embed
if match:
groups = match.group(0).split("\n")[1:-1]
value = ""
for index in range(0, len(groups)):
regex = r'<a href=\"/\?(?P<term_url>.*?)\"\>(?P<term>.*?)</a>'
match = re.search(regex, groups[index])
if match:
value += ("[{}](https://senseis.xmp.net/?" +
"{})\n").format(match.group("term"),
match.group("term_url"))
embed.add_field(name='Alternative search terms:',
value=value,
inline=False)
else:
embed.add_field(name="Alternative search terms",
value="No alternative terms found.", inline=False)
add_footer(embed, ctx.author)
await ctx.send(embed=embed)
async def check_KGS():
await bot.wait_until_ready()
global kgs_to_send
# Display a message for which time was bot last updated.
channel = bot.get_channel(channels["testing-bots"])
msg = "{} was just deployed.".format(bot.user.mention)
await channel.send(msg)
await get_roles()
async with aiohttp.ClientSession() as kgs_session:
await kgs.login(kgs_session)
while not bot.is_closed == True:
await kgs.send_kgs_messages(kgs_session, kgs_to_send)
kgs_to_send = []
await kgs.get_messages(kgs_session, bot)
await asyncio.sleep(1)
bot.loop.create_task(check_KGS())
bot.run(os.environ["OSR_TOKEN"])