Merge pull request #4 from mightyBroccoli/dev

+ added !xep feature
This commit is contained in:
nico 2018-10-03 23:40:12 +02:00 committed by GitHub
commit 1987753e3f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 127 additions and 17 deletions

2
.gitignore vendored
View file

@ -60,4 +60,6 @@ target/
# .idea # .idea
.idea .idea
.etag
bot\.cfg bot\.cfg
xeplist.xml

View file

@ -93,7 +93,7 @@ class ContactInfo:
# class handeling XMPPError exeptions # class handeling XMPPError exeptions
class HandleError: class HandleError:
def __init__(self, error, msg, key, target): def __init__(self, error, msg, key, target="target missing"):
self.error = error self.error = error
self.message = msg self.message = msg
self.key = key self.key = key

View file

@ -12,7 +12,8 @@ class StaticAnswers:
'help': '!help -- display this text', 'help': '!help -- display this text',
'version': '!version domain.tld -- receive XMPP server version', 'version': '!version domain.tld -- receive XMPP server version',
'uptime': '!uptime domain.tld -- receive XMPP server uptime', 'uptime': '!uptime domain.tld -- receive XMPP server uptime',
'contact': '!contact domain.tld -- receive XMPP server contact address info'} 'contact': '!contact domain.tld -- receive XMPP server contact address info',
'xep': '!xep XEP Number -- recieve information about the specified XEP'}
self.possible_answers = { self.possible_answers = {
'1': 'I heard that, %s.', '1': 'I heard that, %s.',
'2': 'I am sorry for that %s.', '2': 'I am sorry for that %s.',
@ -22,8 +23,10 @@ class StaticAnswers:
'2': 'not a valid target' '2': 'not a valid target'
} }
self.keywords = { self.keywords = {
"keywords": ["!help", "!uptime", "!version", "!contact"], "keywords": ["!help", "!uptime", "!version", "!contact", "!xep"],
"no_arg_keywords": ["!help"] "domain_keywords": ["!uptime", "!version", "!contact"],
"no_arg_keywords": ["!help"],
"number_keywords": ["!xep"]
} }
def keys(self, arg="", keyword='keywords'): def keys(self, arg="", keyword='keywords'):

87
classes/xep.py Normal file
View file

@ -0,0 +1,87 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import requests
import defusedxml.ElementTree as ET
class XEPRequest:
def __init__(self, msg, xepnumber):
"""
class which requests the header of the referenced xep
:param xepnumber: number int or str to request the xep for
"""
self.message_type = msg['type']
self.muc_nick = msg['mucnick']
self.reqxep = str(xepnumber)
self.xeplist = None
self.acceptedxeps = list()
def req_xeplist(self):
"""
query and save the current xep list to reduce network bandwidth
"""
try:
with open(".etag") as file:
local_etag = file.read()
except FileNotFoundError:
local_etag = ""
with requests.Session() as s:
s.headers.update({'Accept': 'application/xml'})
head = s.head("https://xmpp.org/extensions/xeplist.xml")
etag = head.headers['etag']
if local_etag == etag:
with open("xeplist.xml", "r") as file:
self.xeplist = ET.fromstring(file.read())
else:
r = s.get("https://xmpp.org/extensions/xeplist.xml")
r.encoding = 'utf-8'
local_etag = head.headers['etag']
with open("xeplist.xml", "w") as file:
file.write(r.content.decode())
self.xeplist = ET.fromstring(r.content.decode())
with open('.etag', 'w') as string:
string.write(local_etag)
# populate xep comparison list
for xep in self.xeplist.findall(".//*[@accepted='true']/number"):
self.acceptedxeps.append(xep.text)
def get(self):
"""
function to query the xep entry if xepnumber is present in xeplist
:return: nicely formatted xep header information
"""
# check if xeplist is accurate
self.req_xeplist()
result = list()
# if requested number is inside acceptedxeps continou
if self.reqxep in self.acceptedxeps:
searchstring = ".//*[@accepted='true']/[number='%s']" % self.reqxep
for item in self.xeplist.findall(searchstring):
for x in range(1,5):
result.append(item[x].tag + " : " + item[x].text)
else:
if self.message_type == "groupchat":
result.append(self.muc_nick + " : " + "XEP-" + str(self.reqxep) + " : is not available.")
else:
result.append("XEP-" + str(self.reqxep) + " : is not available.")
return result
def format(self):
reply = self.get()
if self.message_type == "groupchat":
text = "%s: " % self.muc_nick
reply[0] = text + reply[0]
text = '\n'.join(reply)
return text

44
main.py
View file

@ -19,6 +19,7 @@ from slixmpp.exceptions import XMPPError
from classes.strings import StaticAnswers from classes.strings import StaticAnswers
from classes.functions import Version, LastActivity, ContactInfo, HandleError from classes.functions import Version, LastActivity, ContactInfo, HandleError
from classes.xep import XEPRequest
class QueryBot(slixmpp.ClientXMPP): class QueryBot(slixmpp.ClientXMPP):
@ -36,7 +37,7 @@ class QueryBot(slixmpp.ClientXMPP):
def start(self, event): def start(self, event):
""" """
:param str event -- An empty dictionary. The session_start event does not provide any additional data. :param event -- An empty dictionary. The session_start event does not provide any additional data.
""" """
self.send_presence() self.send_presence()
self.get_roster() self.get_roster()
@ -47,27 +48,41 @@ class QueryBot(slixmpp.ClientXMPP):
def validate_domain(self, wordlist, index): def validate_domain(self, wordlist, index):
""" """
validation method to reduce connection attemps to unvalid domains validation method to reduce malformed querys and unnecessary connection attempts
:param wordlist: words seperated by " " from the message :param wordlist: words separated by " " from the message
:param index: keyword index inside the message :param index: keyword index inside the message
:return: true if valid :return: true if valid
""" """
# keyword inside the message # keyword inside the message
argument = wordlist[index] argument = wordlist[index]
# if the argument is not inside the no_arg_keywords target is index + 1 # check if argument is in the argument list
if argument not in StaticAnswers().keys(arg='list', keyword="no_arg_keywords"): if argument in StaticAnswers().keys(arg='list'):
try: # if argument uses a domain check for occurence in list and check domain
target = wordlist[index + 1] if argument in StaticAnswers().keys(arg='list', keyword='domain_keywords'):
if validators.domain(target): try:
target = wordlist[index + 1]
if validators.domain(target):
return True
except IndexError:
# except an IndexError if a keywords is the last word in the message
return False
# check if number keyword is used if true check if target is assignable
elif argument in StaticAnswers().keys(arg='list', keyword='number_keywords'):
try:
target = wordlist[index + 1]
return True return True
except IndexError: except IndexError:
# except an IndexError if a keywords is the last word in the message # except an IndexError if target is not assignable
return False
# check if argument is inside no_arg list
elif argument in StaticAnswers().keys(arg='list', keyword="no_arg_keywords"):
return True
else:
return False return False
elif argument in StaticAnswers().keys(arg='list', keyword="no_arg_keywords"):
return True
else: else:
return return False
def deduplicate(self, reply): def deduplicate(self, reply):
""" """
@ -136,6 +151,9 @@ class QueryBot(slixmpp.ClientXMPP):
contact = yield from self['xep_0030'].get_info(jid=target, cached=False) contact = yield from self['xep_0030'].get_info(jid=target, cached=False)
reply.append(ContactInfo(contact, msg, target).format_contact()) reply.append(ContactInfo(contact, msg, target).format_contact())
elif keyword == "!xep":
reply.append(XEPRequest(msg, target).format())
except XMPPError as error: except XMPPError as error:
reply.append(HandleError(error, msg, key, target).build_report()) reply.append(HandleError(error, msg, key, target).build_report())