Skip to content

Commit 80433e0

Browse files
committed
Merge v0.2
2 parents 4ed5716 + 21d96aa commit 80433e0

9 files changed

+442
-364
lines changed

.travis.yml

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
language: python
2+
python:
3+
- "3.4"
4+
- "3.5"
5+
6+
script: nosetests

user.py renamed to beam.py

+43-23
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,28 @@
11
from logging import getLogger as get_logger
2-
from logging import WARNING
2+
from logging import INFO, WARNING, FileHandler
33
from requests import Session
44
from json import dumps, loads
55
from websockets import connect
66

77

8-
class User:
8+
class Beam:
99
path = "https://beam.pro/api/v1"
1010

11+
message_id = 0
12+
1113
def __init__(self, debug="WARNING", **kwargs):
12-
self._init_logger(debug)
13-
self.session = Session()
14+
self._init_logger(debug, kwargs.get("log_to_file", False))
15+
self.http_session = Session()
1416

15-
def _init_logger(self, level):
17+
def _init_logger(self, level, log_to_file=False):
1618
"""Initialize logger."""
1719

18-
self.logger = get_logger('CactusBot')
20+
self.logger = get_logger("CactusBot")
21+
22+
if log_to_file:
23+
file_handler = FileHandler("latest.log")
24+
file_handler.setLevel(INFO)
25+
self.logger.addHandler(file_handler)
1926

2027
if level is True:
2128
level = "DEBUG"
@@ -43,40 +50,47 @@ def _init_logger(self, level):
4350

4451
self.logger.info("Logger initialized!")
4552

46-
def request(self, req, url, **kwargs):
53+
def _request(self, req, url, **kwargs):
4754
"""Send HTTP request to Beam."""
48-
if req.lower() in ('get', 'head', 'post', 'put', 'delete', 'options'):
55+
if req.lower() in ("get", "head", "post", "put", "delete", "options"):
4956
if req.lower() == "get":
50-
response = self.session.get(
57+
response = self.http_session.get(
5158
self.path + url,
5259
params=kwargs.get("params")
5360
)
5461
else:
55-
response = self.session.__getattribute__(req.lower())(
62+
response = self.http_session.__getattribute__(req.lower())(
5663
self.path + url,
5764
data=kwargs.get("data")
5865
)
5966

60-
if 'error' in response.json().keys():
61-
self.logger.warn("Error: {}".format(response.json()['error']))
62-
63-
return response.json()
67+
try:
68+
json = response.json()
69+
except ValueError:
70+
return None
71+
else:
72+
if "error" in json.keys():
73+
self.logger.warn("Error: {}".format(json["error"]))
74+
return json
6475
else:
6576
self.logger.debug("Invalid request: {}".format(req))
6677

6778
def login(self, username, password, code=''):
6879
"""Authenticate and login with Beam."""
69-
l = locals()
70-
packet = {n: l[n] for n in ("username", "password", "code")}
71-
return self.request("POST", "/users/login", data=packet)
80+
packet = {
81+
"username": username,
82+
"password": password,
83+
"code": code
84+
}
85+
return self._request("POST", "/users/login", data=packet)
7286

7387
def get_channel(self, id, **p):
7488
"""Get channel data by username."""
75-
return self.request("GET", "/channels/{id}".format(id=id), params=p)
89+
return self._request("GET", "/channels/{id}".format(id=id), params=p)
7690

7791
def get_chat(self, id):
7892
"""Get chat server data."""
79-
return self.request("GET", "/chats/{id}".format(id=id))
93+
return self._request("GET", "/chats/{id}".format(id=id))
8094

8195
def connect(self, channel_id, bot_id):
8296
"""Connect to a Beam chat through a websocket."""
@@ -106,21 +120,27 @@ def send_message(self, arguments, method="msg"):
106120
if isinstance(arguments, str):
107121
arguments = (arguments,)
108122

109-
msg_packet = {
123+
message_packet = {
110124
"type": "method",
111125
"method": method,
112126
"arguments": arguments,
113127
"id": self.message_id
114128
}
115129

116-
yield from self.websocket.send(dumps(msg_packet))
130+
if method == "msg":
131+
self.logger.info("$[CactusBot] {message}".format(
132+
message=arguments[0]))
133+
134+
yield from self.websocket.send(dumps(message_packet))
117135
self.message_id += 1
118136

119-
return (yield from self.websocket.recv())
137+
if method in ("msg", "auth"):
138+
return (yield from self.websocket.recv())
139+
return True
120140

121141
def remove_message(self, channel_id, message_id):
122142
"""Remove a message from chat."""
123-
return self.request("DELETE", "/chats/{id}/message/{message}".format(
143+
return self._request("DELETE", "/chats/{id}/message/{message}".format(
124144
id=channel_id, message=message_id))
125145

126146
def read_chat(self, handle=None):

cactus.py

+54-17
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
# CactusBot!
22

33
from messages import MessageHandler
4-
from user import User
4+
from beam import Beam
55

66
from os.path import exists
7-
from time import sleep
8-
from json import load
7+
from sys import exit
8+
from json import load, dump
99
from shutil import copyfile
10+
from functools import reduce
1011

1112
from asyncio import get_event_loop, gather, async
1213

1314
from traceback import format_exc
15+
from time import sleep
1416

1517
from models import Base, engine
1618

@@ -38,7 +40,7 @@
3840
"""
3941

4042

41-
class Cactus(MessageHandler, User):
43+
class Cactus(MessageHandler, Beam):
4244
started = False
4345
connected = False
4446
message_id = 0
@@ -48,12 +50,13 @@ def __init__(self, autorestart=True, **kwargs):
4850
self.debug = kwargs.get("DEBUG", False)
4951
self.autorestart = autorestart
5052
self.config_file = kwargs.get("config_file", "data/config.json")
53+
self.stats_file = kwargs.get("stats_file", "data/stats.json")
5154
self.database = kwargs.get("database", "data/data.db")
5255

53-
def check_db(self):
56+
def _init_database(self, database):
5457
"""Ensure the database exists."""
5558

56-
if exists(self.database):
59+
if exists(database):
5760
self.logger.info("Found database.")
5861
else:
5962
self.logger.info("Database wasn't found.")
@@ -67,22 +70,54 @@ def load_config(self, filename):
6770
"""Load configuration."""
6871

6972
if exists(filename):
70-
self.logger.info("Config file was found. Loading...")
73+
self.logger.info("Configuration file was found. Loading...")
74+
self.config_file = filename
7175
with open(filename) as config:
7276
self.config = load(config)
73-
return True
77+
return self.config
7478
else:
75-
self.logger.warn("Config file was not found. Creating...")
79+
self.logger.warn("Configuration file was not found. Creating...")
7680
copyfile("data/config-template.json", filename)
7781
self.logger.error(
78-
"Config created. Please enter information, and restart.")
79-
raise FileNotFoundError("Config not found.")
82+
"Configuration file created. Please enter values and restart.")
83+
raise FileNotFoundError("Configuration not found.")
84+
exit()
85+
86+
def load_stats(self, filename):
87+
if exists(filename):
88+
self.stats_file = filename
89+
self.logger.info("Statistics file was found. Loading...")
90+
with open(filename) as stats:
91+
self.stats = load(stats)
92+
return self.stats
93+
else:
94+
self.logger.warn("Statistics file was not found. Creating...")
95+
copyfile("data/stats-template.json", "data/stats.json")
96+
self.logger.info("Statistics file created.")
97+
98+
def update_config(self, keys, value):
99+
with open(self.config_file, 'r') as config:
100+
config_data = load(config)
101+
reduce(lambda d, k: d[k], keys.split('.')[:-1], config_data)[
102+
keys.split('.')[-1]] = value
103+
with open(self.config_file, 'w+') as config:
104+
dump(config_data, config, indent=4, sort_keys=True)
105+
self.config = config_data
106+
107+
def update_stats(self, keys, value):
108+
with open(self.stats_file, 'r') as stats:
109+
stats_data = load(stats)
110+
reduce(lambda d, k: d[k], keys.split('.')[:-1], stats_data)[
111+
keys.split('.')[-1]] = value
112+
with open(self.config_file, 'w+') as config:
113+
dump(stats_data, config, indent=4, sort_keys=True)
114+
self.config = stats_data
80115

81116
def run(self, *args, **kwargs):
82117
"""Run bot."""
83118

84119
self.logger.info(cactus_art)
85-
self.check_db()
120+
self._init_database(self.database)
86121

87122
while self.autorestart or not self.started:
88123
try:
@@ -91,11 +126,11 @@ def run(self, *args, **kwargs):
91126
loop = get_event_loop()
92127

93128
self.connected = bool(loop.run_until_complete(
94-
self.connect(self.channel_data['id'], self.bot_data['id'])
129+
self.connect(self.channel_data["id"], self.bot_data["id"])
95130
))
96131

97132
self.logger.info("{}uccessfully connected to chat {}.".format(
98-
['Uns', 'S'][self.connected], self.channel_data["token"]
133+
["Uns", "S"][self.connected], self.channel_data["token"]
99134
))
100135

101136
if self.connected:
@@ -137,8 +172,8 @@ def _run(self, *args, **kwargs):
137172
"""Bot execution code."""
138173

139174
if self.load_config(filename=self.config_file):
140-
auth = {n: self.config[n] for n in ("username", "password")}
141-
self.bot_data = self.login(**auth)
175+
self.load_stats(filename=self.stats_file)
176+
self.bot_data = self.login(**self.config["auth"])
142177
self.username = self.bot_data["username"]
143178
self.bot_id = self.bot_data["id"]
144179
self.logger.info("Authenticated as: {}.".format(self.username))
@@ -153,7 +188,9 @@ def _run(self, *args, **kwargs):
153188
status=["offline", "online"][self.channel_data["online"]]
154189
))
155190

191+
self._init_commands()
192+
156193

157194
if __name__ == "__main__":
158-
cactus = Cactus(debug="info", autorestart=False)
195+
cactus = Cactus(debug="debug", autorestart=False, log_to_file=True)
159196
cactus.run()

data/config-template.json

+21-9
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,23 @@
11
{
2-
"username": "USERNAME",
3-
"password": "PASSWORD",
4-
"channel": "CHANNELNAME",
5-
"points_name": "Thorns",
6-
"points_per_interval": 5,
7-
"points_enabled": true,
8-
"interval": 1,
9-
"announce_enter": false,
10-
"announce_leave": false
2+
"channel": "CHANNEL",
3+
"auth": {
4+
"username": "USERNAME",
5+
"password": "PASSWORD"
6+
},
7+
"spam_protection": {
8+
"maximum_message_length": 256,
9+
"maximum_message_capitals": 32,
10+
"maximum_message_emotes": 8,
11+
"allow_links": false
12+
},
13+
"announcements": {
14+
"announce_enter": false,
15+
"announce_leave": false
16+
},
17+
"points": {
18+
"name": "coin",
19+
"per_interval": 5,
20+
"interval": 1,
21+
"enabled": true
22+
}
1123
}

liveloading.py

-13
This file was deleted.

0 commit comments

Comments
 (0)