Skip to content

Commit bf5979a

Browse files
author
crash
committed
initial codebase
0 parents  commit bf5979a

File tree

6 files changed

+375
-0
lines changed

6 files changed

+375
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/config.cfg
2+
/__pycache__

AeAPI.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import threading
2+
import json
3+
import urllib.request
4+
import urllib.parse
5+
6+
class AeAPI:
7+
url = "http://ae27ff.localhost/api/"
8+
appname = 'Unknown-PyAeAPI-App'
9+
appid = ''
10+
appver = '1.0'
11+
apiver='1.0'
12+
13+
@classmethod
14+
def query_preparedata(cls,data_fields):
15+
if data_fields is None:
16+
return None
17+
json_fields = {k: json.dumps(v) for k,v in data_fields.items()}
18+
return urllib.parse.urlencode(json_fields).encode('utf-8');
19+
20+
@classmethod
21+
def query(cls,qname,fields={},data_fields={}):
22+
result = {}
23+
try:
24+
fields['query']=qname
25+
fields['appid']=cls.appid
26+
arguments = urllib.parse.urlencode(fields)
27+
qurl = cls.url + "?"+ arguments
28+
req = urllib.request.Request(
29+
qurl,
30+
data=cls.query_preparedata(data_fields),
31+
headers={
32+
'User-Agent':'PyAeAPI/'+cls.apiver+' '+cls.appname+'/'+cls.appver,
33+
"Content-Type": "application/x-www-form-urlencoded"
34+
}
35+
)
36+
result = json.loads(urllib.request.urlopen(req).read().decode('utf-8'))
37+
except Exception as e:
38+
result={'neterror':True,'error':e}
39+
else:
40+
result['neterror']=False;
41+
return result
42+
43+
@classmethod
44+
def usersonrank(cls,rank):
45+
return cls.query('usersonrank',{'rank':rank})
46+
47+
@classmethod
48+
def progressions(cls,user='*',limit=100):
49+
return cls.query('progressions',{'user':user,'limit':limit})
50+
51+
@classmethod
52+
def lastrankup(cls):
53+
return cls.query('lastrankup')
54+
55+
@classmethod
56+
def userrank(cls,user):
57+
return cls.query('userrank',{'user':user})
58+
59+
@classmethod
60+
def bulkrank(cls,users):
61+
return cls.query('bulkrank',{},{'users':users});
62+
63+
@classmethod
64+
def activity(cls,days=None):
65+
if days is None:
66+
return cls.query('activity')
67+
return cls.query('activity',{'days':days})
68+
69+
# timer_func = None
70+
# def timershim():
71+
# if AeAPI.timer_func is not None:
72+
# print('run timer')
73+
# AeAPI.timer_func()
74+
# threading.Timer(60, AeAPI.timershim).start()
75+
#
76+
#
77+
# def timer(timerfunc):
78+
# print('set timer func')
79+
# AeAPI.timer_func = timerfunc
80+
# AeAPI.timershim()
81+
82+

AeRankings.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import json
2+
3+
from AeAPI import *
4+
5+
class AeRankings:
6+
filename = "rankings.json"
7+
rankings = {}
8+
updated = False
9+
10+
@classmethod
11+
def load(cls):
12+
try:
13+
cls.rankings = json.load(open(cls.filename))
14+
except FileNotFoundError:
15+
print("Rankings file not initialized.");
16+
17+
@classmethod
18+
def save(cls):
19+
json.dump(cls.rankings, open(cls.filename, 'w'))
20+
21+
@classmethod
22+
def update(cls):
23+
users = list(cls.rankings.keys())
24+
result = AeAPI.bulkrank(users)
25+
entries = result['users']
26+
for entry in entries:
27+
cls.rankings[entry['user']] = entry['rank']
28+
cls.updated = True
29+
30+
@classmethod
31+
def adduser(cls,user):
32+
cls.rankings[user]=-1 # add user regardless of API exception
33+
result = AeAPI.userrank(user)
34+
#print("userrank dump: ")
35+
#print(result)
36+
cls.rankings[user]=result['rank']
37+
cls.updated = True

DiscordPins.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import discord
2+
import json
3+
4+
class DiscordPins:
5+
filename = "pins.json"
6+
client = None
7+
pins = {}
8+
9+
@classmethod
10+
def load(cls,client):
11+
cls.client = client
12+
try:
13+
cls.pins = json.load(open(cls.filename))
14+
except FileNotFoundError:
15+
print("Pins file not initialized.");
16+
17+
@classmethod
18+
def save(cls):
19+
json.dump(cls.pins, open(cls.filename, 'w'))
20+
21+
@classmethod
22+
def getmidfromcid(cls, channelid):
23+
try:
24+
return cls.pins[channelid]
25+
except KeyError:
26+
return None
27+
28+
@classmethod
29+
def addid(cls,channelid,messageid):
30+
cls.pins[channelid]=messageid
31+
#cls.pins.append( {"channel":channelid, "message":messageid} );
32+
33+
@classmethod
34+
async def getfromid(cls,channelid,messageid=None):
35+
if messageid == None:
36+
messageid = cls.getmidfromcid(channelid)
37+
if messageid == None:
38+
return None
39+
try:
40+
channel = cls.client.get_channel(channelid)
41+
if channel == None:
42+
print("couldn't find channel "+str(channelid))
43+
return None
44+
return await cls.client.get_message(channel, messageid)
45+
except discord.NotFound:
46+
return None
47+
48+
@classmethod
49+
async def removeid(cls,channelid):
50+
message = await cls.getfromid(channelid)
51+
if message == None:
52+
return False
53+
await cls.client.delete_message(message)
54+
cls.pins.pop(channelid,None)
55+
return True
56+
57+
@classmethod
58+
async def remove(cls,channel):
59+
return await cls.removeid(channel.id)
60+
61+
@classmethod
62+
def add(cls,message):
63+
cls.addid(message.channel.id,message.id)
64+
65+
@classmethod
66+
async def editall(cls,new_content=None,embed=None):
67+
keys = list(cls.pins.keys())
68+
for channelid in keys:
69+
print("edit message for channel: " + channelid)
70+
message = await cls.getfromid(channelid)
71+
print(" message found: "+str(message))
72+
if message == None:
73+
cls.pins.pop(channelid,None)
74+
continue
75+
await cls.client.edit_message(message, new_content, embed=embed)
76+
77+
@classmethod
78+
async def create(cls,channel,content=None,embed=None):
79+
message = await cls.client.send_message(channel,content,embed=embed)
80+
cls.add(message)
81+
return message
82+

config.cfg.example

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[discord]
2+
privkey=YOUR_DISCORD_BOT_PRIVKEY_HERE
3+
admins=YOUR_DISCORD_ID_HERE
4+
5+
[ae27ff]
6+
appid=YOUR_AE27FF_APPID_KEY_HERE
7+
appname=ae-Bot
8+
appver=0.1

main.py

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
import discord
2+
import asyncio
3+
import json
4+
import configparser
5+
6+
from AeAPI import *
7+
from AeRankings import *
8+
from DiscordPins import *
9+
10+
config = configparser.ConfigParser()
11+
client = discord.Client()
12+
clientkey = None
13+
description = '''Placeholder commands description'''
14+
15+
def getconfig(section,key,default):
16+
if not config.has_option(section,key):
17+
return default
18+
return config.get(section,key)
19+
20+
def load():
21+
global client
22+
global clientkey
23+
config.read('config.cfg')
24+
AeAPI.appid = getconfig('ae27ff','appid','ae27ff-example-app-id-000-0000-000000000000')
25+
AeAPI.appname = getconfig('ae27ff','appname','ae-Bot')
26+
AeAPI.appver = getconfig('ae27ff','appver','0.1')
27+
clientkey = getconfig('discord','privkey','NO-PRIVKEY-SPECIFIED')
28+
AeRankings.load()
29+
DiscordPins.load(client)
30+
31+
def save():
32+
DiscordPins.save()
33+
AeRankings.save()
34+
35+
def start():
36+
client.loop.create_task(my_background_task())
37+
client.run(clientkey)
38+
39+
def stop():
40+
save()
41+
42+
43+
44+
45+
46+
##########################################################################
47+
48+
def get_activity_string():
49+
gstatus='error'
50+
try:
51+
gstatus = "ae27ff - "+str(AeAPI.activity()['active'])+" users. "+str(AeAPI.activity(1)['active'])+" on today."
52+
print(gstatus)
53+
except Exception as e:
54+
print(e)
55+
print(AeAPI.activity())
56+
print(AeAPI.activity(1))
57+
return gstatus
58+
59+
60+
def link(text,uri=""):
61+
return "["+text+"](https://ae27ff.meme.tips"+uri+")"
62+
63+
def field(level,users):
64+
f = {}
65+
userlinks = "";
66+
for user in users:
67+
userlinks+=link(user,"/u/"+user)+" "
68+
f['name'] = "__**Level "+str(level)+"**__";
69+
f['value'] = userlinks
70+
f['inline']=False
71+
return f
72+
73+
def userstofields():
74+
fields = []
75+
for rank in range(100,0,-1):
76+
users = [u for (u,r) in AeRankings.rankings.items() if r == rank]
77+
if len(users)>0:
78+
fields.append(field(rank,users))
79+
return fields
80+
81+
82+
async def my_background_task():
83+
prefix="!ae"
84+
preEmbedMessage = '**Add your name to the rankings!** \nJust use the command: ```http\n'+prefix+'adduser YourAE27FFName```\n\n**Solved a new level and want to show your progress on the rankings?** \nJust use the command: ```http\n'+prefix+'update```\n\nFor bot managing commands (only for moderators) use: ```http\n'+prefix+'modHelp```';
85+
86+
# embed = discord.Embed(
87+
# title="***AE27FF RANKINGS***",
88+
# type="rich",
89+
# description="List of member's current levels on ",
90+
# colour=11413503,
91+
92+
93+
embed = discord.Embed(**{
94+
"title": "**AE27FF RANKINGS**",
95+
"description": "Member levels on " + link("ae27ff"),
96+
"color": 11413503,
97+
});
98+
99+
embed.set_footer(**{
100+
"icon_url": "https://cdn.discordapp.com/avatars/431930297328730114/da26cbbc0f89134763b03396783a96cb.png",
101+
"text": "Bot inspired by Luis"
102+
});
103+
104+
105+
106+
await client.wait_until_ready()
107+
while not client.is_closed:
108+
AeRankings.save()
109+
DiscordPins.save()
110+
AeRankings.update()
111+
embed.clear_fields()
112+
for field in userstofields():
113+
embed.add_field(**field)
114+
await DiscordPins.editall(preEmbedMessage,embed)
115+
await client.change_presence(game=discord.Game(name=get_activity_string()))
116+
await asyncio.sleep(60) # task runs every 60 seconds
117+
118+
119+
120+
@client.event
121+
async def on_ready():
122+
print('Logged in as')
123+
print(client.user.name)
124+
print(client.user.id)
125+
print('------')
126+
AeRankings.adduser("crashdemons")
127+
AeRankings.adduser("amone")
128+
AeRankings.update()
129+
print(AeRankings.rankings)
130+
await client.change_presence(game=discord.Game(name='ae27ff'))
131+
132+
@client.event
133+
async def on_message(message):
134+
if message.content.startswith('!aecreate'):
135+
tmp = await DiscordPins.create(message.channel,"Loading content...")
136+
elif message.content.startswith('!aedelete'):
137+
result = await DiscordPins.remove(message.channel)
138+
if result:
139+
await client.send_message(message.channel,"Channel message deleted.")
140+
else:
141+
await client.send_message(message.channel,"Channel message not found.")
142+
elif message.content.startswith('!aeadduser'):
143+
param = message.content[len('!aeadduser '):]
144+
if len(param)>0:
145+
AeRankings.adduser(param)
146+
await client.send_message(message.channel,"Added "+str(param)+" to rankings")
147+
else:
148+
await client.send_message(message.channel,"Which user?")
149+
150+
151+
#this is standard, but the example command is never fired (when using client = commands.Bot(...)), so the discord.ext syntax seems useless(?)
152+
#@bot.command()
153+
#async def aeadduser(user: str):
154+
# AeRankings.adduser(user)
155+
# await bot.say("Added user "+str(user)+" to rankings.")
156+
157+
158+
159+
160+
161+
######################################################################
162+
163+
load()
164+
start()

0 commit comments

Comments
 (0)