""" TBD """ __author__ = "Lukas Mahler" __version__ = "0.2.0" __date__ = "23.09.2021" __email__ = "m@hler.eu" __status__ = "Development" # Default import os import json from time import sleep # Custom import ts3 import dotenv # python-dotenv # Self import gecko import util class MyTeamspeakBot: def __init__(self, conf): """ """ self.host = conf["host"] self.port = conf["port"] self.user = conf["user"] self.pwd = conf["pwd"] self.sid = conf["sid"] self.nickname = conf["name"] self.gecko = gecko.GeckoAPI() self.myid = None self.running = True self.intro = "" print(f"* Trying to connect to: {self.host}:{self.port}") with ts3.query.TS3Connection(self.host, self.port) as self.bot: self.bot.login(client_login_name=self.user, client_login_password=self.pwd) self.bot.use(sid=self.sid) try: self.bot.clientupdate(client_nickname=self.nickname) except ts3.query.TS3QueryError: pass print(f"* Successfully connected as: {self.nickname}") # Start the Bot self.loop() def loop(self): """ """ # Find my client id me = self.bot.clientfind(pattern=self.nickname) if len(me) == 1: self.myid = me[0]["clid"] else: raise ValueError("x Can't find my own client id.") ''' if you want to move the Bot to a certain channel (instead of the defualt channel, you can do: ''' # self.bot.clientmove(clid=self.myid,cid=129) # Subscribe to a server movement events self.bot.servernotifyregister(event="server") # Subscribe to server channel messages self.bot.servernotifyregister(event="textserver") # Subscribe to channel messages self.bot.servernotifyregister(event="textchannel") # Subscribe to privat chat messages self.bot.servernotifyregister(event="textprivate") # Subscribe to channel movement events # self.bot.servernotifyregister(event="channel", id_=0) # Start the timer for auto-updating crypto channels channelname = f"{'[cspacerBTC]Bitcoin:':<33}" + f"{self.gecko.getSymbol('BTC', decimal=0):>5}€" btc_timer = util.maketimer(60, self.editChannelname(200, channelname)) btc_timer.start() channelname = f"{'[cspacerETH]Ethereum:':<30}" + f"{self.gecko.getSymbol('ETH', decimal=0):>5}€" eth_timer = util.maketimer(60, self.editChannelname(201, channelname)) eth_timer.start() # Notify connected admins self.notifyAdmin() # ----------- LOOP HERE ------------- while self.running: # self.bot.send_keepalive() print("* Waiting for a new Event...") self.bot.version() try: # This method blocks, but we must sent the keepalive message at # least once in 5 minutes to avoid the sever side idle client # disconnect. So we set the timeout parameter simply to 1 minute. event = self.bot.wait_for_event(timeout=60) except ts3.query.TS3TimeoutError: pass else: event_type = event.event print(f"* Got Event | length={len(event[0])} | {event_type}") # Client Connect if event_type == "notifycliententerview": print(f"* Client [{event[0]['client_nickname']}] connected.") # Check if the connector is a ServerQuery or not if not self.isqueryclient(event[0]["client_unique_identifier"]): print(f"* {event[0]}") # Check if the connector is an Admin if self.isadmin(event[0]["client_database_id"]): self.bot.sendtextmessage(targetmode=1, target=event[0]["clid"], msg=self.intro) else: pass else: pass # Client disconnected elif event_type == "notifyclientleftview": print(f"* Clientid [{event[0]['clid']}] disconnected.") pass # Text Message elif event_type == "notifytextmessage": msg = event[0]["msg"] invkr = event[0]["invokername"] invkr_id = event[0]["invokerid"] print(f'* From: "{invkr}" | Message: "{msg}"') self.lookupcommand(msg, invkr_id) else: print(f"* Unknown Event: {event.__dict__}") def stop(self, invkr_id): """ """ msg = "I'm out, bye bye!" self.bot.sendtextmessage(targetmode=1, target=invkr_id, msg=msg) self.running = False def notifyAdmin(self): clients = self.bot.clientlist() clients = [client for client in clients if client["client_type"] != "1"] for client in clients: cldbid = client["client_database_id"] clid = client["clid"] if self.isadmin(cldbid): self.bot.sendtextmessage(targetmode=1, target=clid, msg=self.intro) sleep(1) # This can be removed if the Query Client is Whitelisted def kickall(self, msg): """ """ clients = self.bot.clientlist() clients = [client["clid"] for client in clients if client["client_type"] != "1"] for clid in clients: try: self.bot.clientpoke(msg=msg, clid=clid) # TODO except: pass def poke(self, msg=None, num=10, delay=0.2, usr='all'): """ """ if msg is None: msg = "-~-~-~-~-~-~-~-~-~-~-~" # Get the client ids if usr == 'all': clients = self.bot.clientlist() clients = [client["clid"] for client in clients if client["client_type"] != "1"] else: clients = self.bot.clientfind(pattern=usr) clients = [client["clid"] for client in clients] # Break, if there's no client. if not clients: return None else: for client in clients: data = self.printable_clientinfo(client) print(f"* {data}") # Nopokeatm # return # Poke them i = 0 while num == -1 or i < num: for clid in clients: print(f"* {clid}") try: self.bot.clientpoke(msg=msg, clid=clid) except: pass sleep(delay) i += 1 return None def createChannel(self, name, permanent=False): """ """ if permanent: new = self.bot.channelcreate(channel_name=name, channel_flag_permanent="1") else: new = self.bot.channelcreate(channel_name=name) return new[0]["cid"] def editChannelname(self, cid, name): """ """ self.bot.channeledit(cid=cid, channel_name=name) sleep(5) def list(self, what, invkr_id): """ """ if what == "channel": mydict = {} channels = self.bot.channellist() for channel in channels: order = channel["channel_order"] mydict[order] = [channel["channel_name"], channel["cid"]] mydict = dict(sorted(mydict.items())) msg = json.dumps(mydict) elif what == "clients": msg = self.bot.clientlist() # TODO else: msg = None self.bot.sendtextmessage(targetmode=1, target=invkr_id, msg=msg) @staticmethod def isqueryclient(cluid): """ Check if the given client-uid is a query client """ # client = self.bot.clientlist(uid=uid) # print(client[0]) # if client[0]["client_type"] == "1": if cluid == "ServerQuery": print("* ISQUERY: True") return True else: print("* ISQUERY: False") return False def isadmin(self, cldbid): """ Check if the given client-databaseid is an admin """ groups = self.bot.servergroupsbyclientid(cldbid=cldbid) # [print(group["sgid"]) for group in groups] for group in groups: # 6 Server Admin/ 13 Operator / 15 Root # if (group["sgid"] == "6") or (group["sgid"] == "13") or (group["sgid"] == "15"): if group["sgid"] == "15": print("* ISADMIN: True") return True else: continue print("* ISADMIN: False") return False def lookupcommand(self, msg, invkr_id): """ """ if msg.startswith("."): commandstring = msg.split(" ") command = commandstring[0] parameter = commandstring[1:] print(f"* command: {command} / parameter: {parameter} / invkr_id: {invkr_id}") if command == ".annoy": try: target = parameter[0] msg = parameter[1] self.poke(msg=msg, usr=target) except IndexError: err = "Please use the command like this: .annoy TARGET MESSAGE" self.bot.sendtextmessage(targetmode=1, target=invkr_id, msg=err) pass elif command == ".kickall": self.kickall("test") # TODO elif command == ".test": cid = self.createChannel("Test") self.bot.sendtextmessage(targetmode=1, target=invkr_id, msg=cid) elif command == ".btc": channelname = f"{'[cspacerBTC]Bitcoin:':<33}" + f"{self.gecko.getSymbol('BTC', decimal=0):>5}€" try: self.editChannelname(200, channelname) except ts3.query.TS3QueryError: pass elif command == ".eth": channelname = f"{'[cspacerETH]Ethereum:':<30}" + f"{self.gecko.getSymbol('ETH', decimal=0):>5}€" try: self.editChannelname(201, channelname) except ts3.query.TS3QueryError: pass elif command == ".list": try: self.list(parameter[0], invkr_id) except IndexError: err = "Please use the command like this: .list channel/clients" self.bot.sendtextmessage(targetmode=1, target=invkr_id, msg=err) pass elif command == ".follow": pass # TODO elif command == ".rename": pass # TODO elif command == ".stop" or command == ".quit" or command == ".q": self.stop(invkr_id) elif command == ".pingall": try: msg = parameter[0] self.poke(msg=msg) except IndexError: err = "Please use the command like this: .pingall MESSAGE" self.bot.sendtextmessage(targetmode=1, target=invkr_id, msg=err) pass else: err = f"Unknown Command: [{command}]" self.bot.sendtextmessage(targetmode=1, target=invkr_id, msg=err) def printable_clientinfo(self, client): """ """ usrd = {} info = self.bot.clientinfo(clid=client) temp = info._data[0].split() for t1 in temp: t2 = t1.decode("utf-8") t3 = t2.split("=") try: t3[1] except Exception: t3.append("None") pass usrd[t3[0]] = t3[1] return usrd def checkgrp(self): """ """ clients = self.bot.clientlist() clients_cldbid = [client["client_database_id"] for client in clients if client["client_type"] != "1"] clients = self.bot.clientlist(groups=True) clients_groups = [client["client_servergroups"] for client in clients if client["client_type"] != "1"] print(f"* {clients_groups}") # ---------------------------------------------------------------------------------------------------------------------- def main(): # Load Dotenv dotenv_file = dotenv.find_dotenv() if not dotenv_file: raise FileNotFoundError("could not locate .env file") dotenv.load_dotenv(dotenv_file) dotend_keys = dotenv.dotenv_values() if not {"_HOST", "_PORT", "_USER", "_PWD", "_SID"} <= dotend_keys.keys(): raise ValueError("missing keys in your .env file.") # Config conf = dict(host=os.getenv("_HOST"), port=os.getenv("_PORT"), user=os.getenv("_USER"), pwd=os.getenv("_PWD"), sid=os.getenv("_SID"), name=os.getenv("_NAME")) # Start the Bot Instance abot = MyTeamspeakBot(conf) if __name__ == "__main__": main()