m4pl1mp/plugins/dsa_plugin.py
2022-07-27 00:17:33 -05:00

639 lines
36 KiB
Python

# -*- coding: utf-8 -*-
####################################################################################### SOF
####################################################################################### SOF
from irc3.plugins.command import command
import irc3
import os
import re
from datetime import datetime, timedelta
import timeago
from irc3.plugins.cron import cron
###########################################################################################
###########################################################################################
D="\x02\x0315"
G="\x02\x0314"
S="\x02\x0304"
P="\x02\x0305"
X="\x02\x0307"
R="\x02\x0302"
B="\x02\x0312"
###########################################################################################
###########################################################################################
class DBIO():
#######################################################################################
#######################################################################################
threshold=30
negated=['matrixbot','van']
lurkers=[]
verified=[]
hardchatters=[]
seen_users=[]
search_users=[]
channel_users=[]
channel=[]
LOG=[]
###########################################################################################
###########################################################################################
@irc3.plugin
class Plugin:
def __init__(self,bot):
self.bot=bot
self.bot.dbio=DBIO()
###################################################################################
###################################################################################
try:
self.bot.dbio.hardchatters=self.bot.db.getlist("hardchatters", [])
print('<<< loaded database >>> hardchatters')
except Exception as e:
print(f'error: dsa_plugin > __init__ > hardchatter storage database: {e}')
self.bot.dbio.hardchatters=[]
###################################################################################
###################################################################################
try:
self.bot.dbio.lurkers=self.bot.db.getlist("lurkers", [])
print('<<< loaded database >>> lurkers')
except Exception as e:
print(f'error: dsa_plugin > __init__ > lurkers storage database: {e}')
self.bot.dbio.lurkers=[]
#######################################################################################
#######################################################################################
def log(self,s):
self.bot.dbio.LOG.append(s)
#######################################################################################
#######################################################################################
def log_play(self,target):
for _ in self.bot.dbio.LOG:
self.bot.privmsg(target,_); print(_)
self.bot.dbio.LOG=[]
#######################################################################################
#######################################################################################
def dbentry_add(self,database,record):
buffer=self.bot.db.getlist(database,[])
buffer.append(record)
buffer=list(set(buffer))
buffer.sort()
exec(f'self.bot.dbio.{database}=buffer')
self.bot.db.setlist(database,buffer)
print(f'added {record} entry to {database} database')
#######################################################################################
#######################################################################################
def dbentry_remove(self,database,record):
buffer=self.bot.db.getlist(database,[])
status=False
if record in buffer:
buffer.remove(record)
status=True
buffer=list(set(buffer))
buffer.sort()
exec(f'self.bot.dbio.{database}=buffer')
self.bot.db.setlist(database,buffer)
print(f'removed {record} entry from {database} database - status: {status}')
#######################################################################################
#######################################################################################
def epochtimestamp(self,timestamp):
year,month,day=timestamp.split('T')[0].split('-')
hour,minute,second=timestamp.split('T')[1].split(':')
second,millisecond=second.split('.')
year,month,day,hour,minute,second,millisecond=[int(x) for x in [year,month,day,hour,minute,second,millisecond]]
epoch_timestamp_past=datetime(year,month,day,hour,minute,second,millisecond).timestamp()
return epoch_timestamp_past
#######################################################################################
#######################################################################################
def epochdelta(self,timestamp):
year,month,day=timestamp.split('T')[0].split('-')
hour,minute,second=timestamp.split('T')[1].split(':')
second,millisecond=second.split('.')
year,month,day,hour,minute,second,millisecond=[int(x) for x in [year,month,day,hour,minute,second,millisecond]]
epoch_timestamp_past=datetime(year,month,day,hour,minute,second,millisecond).timestamp()
epoch_timestamp_present=datetime.now().timestamp()
epochdelta=epoch_timestamp_present-epoch_timestamp_past
distance=timeago.format(timedelta(seconds=epochdelta),datetime.now())
return distance
#######################################################################################
#######################################################################################
@irc3.event(irc3.rfc.NEW_NICK)
def on_nick_change_for_dsa_hardchatradar(self, nick, new_nick):
for _ in self.bot.channels.keys():
if _[0]=="#":
USER=str(new_nick).lower()
self.dsa_hardchatradar(USER,_)
#######################################################################################
#######################################################################################
@irc3.event(irc3.rfc.JOIN)
def on_channel_join_for_dsa_hardchatradar(self,mask,channel,**kw):
USER=str(mask.nick).lower
self.dsa_hardchatradar(mask.nick,channel)
#######################################################################################
#######################################################################################
def dsa_hardchatradar(self, USER, channel):
"""radar for hardchatters who join a channel"""
###############################################################################
###############################################################################
try:
if USER != self.bot.nick:
#######################################################################
key=self.bot.db.getlist(f'last_msg_for_{USER}')
#######################################################################
if key==None:
if USER in self.bot.dbio.negated:
if not USER in self.bot.dbio.hardchatters:
self.dbentry_add('hardchatters',USER)
print(f'{USER}:{channel} {B}>>> {X}whitelisted user {G}- {X}negated')
else:
print(f'{USER}:{channel} {B}>>> {X}whitelisted user {G}- {X}negated - already in db')
else:
if not USER in self.bot.dbio.lurkers:
self.dbentry_add('lurkers',USER)
self.log(f'{G}{USER}:{channel} {S}> rando - set lurker status - no interaction history')
else:
print(f'join {USER}:{channel} >>> lurker - already in db')
#######################################################################
else:
records_timestamps=[]
records_privmsgs=[]
records_newnicks=[]
for _key in key:
timestamp=self.epochdelta(_key['time'])
records_timestamps.append(_key['time'])
if _key['type']=='privmsg':
records_privmsgs.append(timestamp)
else:
records_newnicks.append(timestamp)
#######################################################################
records_timestamps.sort()
newest_timestamp=self.epochdelta(records_timestamps[-1])
records_timestamps.reverse()
oldest_timestamp=self.epochdelta(records_timestamps[-1])
oldest_timestamp_epoch=self.epochtimestamp(records_timestamps[-1])
history_timestamp=datetime.now().timestamp()-oldest_timestamp_epoch
#######################################################################
lurker_status=False
hardchat_status=False
history_status=False
if history_timestamp>=self.bot.dbio.threshold:
history_status=True
if not USER in self.bot.dbio.hardchatters:
self.dbentry_add('hardchatters',USER)
print(f'join {USER}:{channel} > history match - new entry added to hardchatters database')
if USER in self.bot.dbio.hardchatters:
hardchat_status=True
if USER in self.bot.dbio.lurkers:
lurker_status=True
#######################################################################
if records_privmsgs==0:
self.dbentry_add('lurkers',USER)
lurker_status=True
else:
if history_status or hardchat_status and not lurker_status:
cmd=f"MODE {channel} +v {USER}"
self.bot.send(cmd)
print(f'join {USER}:{channel} > pass - history_status: {history_status} - hardchat_status: {hardchat_status} - lurker_status: {lurker_status}')
else:
print(f'join {USER}:{channel} > fail - history_status: {history_status} - hardchat_status: {hardchat_status} - lurker_status: {lurker_status}')
except Exception as e:
msg=f'error: dsa_plugin:hardchat_radar - {e}'
print(msg); self.bot.privmsg(channel,self.bot.emo(msg))
#######################################################################################
#######################################################################################
@command(permission='view')
def dsa_hardchatter(self, mask, target, args):
"""dsa_hardchatter
%%dsa_hardchatter <message>...
"""
try:
OP_FLAG=False
channel = self.bot.channels[target]
for _, nick in channel.modes.items():
for __ in _:
modes=["%","@","&","~"]
if __ in modes:
if mask.nick in nick:
OP_FLAG=True
if OP_FLAG:
user = ''.join(args['<message>'])
self.dbentry_add('hardchatters',user)
self.bot.privmsg(target, self.bot.emo(f'hardchatter: {user}'))
cmd=f"MODE {target} +v {user}"
self.bot.send(cmd)
else:
return
except Exception as e:
msg=f'error: dsa_plugin:hardchatter > {e}'
print(msg); self.bot.privmsg(target,self.bot.emo(msg))
#######################################################################################
#######################################################################################
@command(permission='view')
def dsa_softchatter(self, mask, target, args):
"""dsa_softchatter
%%dsa_softchatter <message>...
"""
user = ''.join(args['<message>'])
try:
OP_FLAG=False
channel = self.bot.channels[target]
for _, nick in channel.modes.items():
for __ in _:
modes=["%","@","&","~"]
if __ in modes:
if mask.nick in nick:
OP_FLAG=True
if OP_FLAG:
self.dbentry_remove('hardchatters',user)
self.bot.privmsg(target, self.bot.emo(f'softchatted: {user}'))
cmd=f"MODE {target} -v {user}"
self.bot.send(cmd)
else:
return
except Exception as e:
msg=f'error: dsa_plugin:softchatter > {e}'
print(msg); self.bot.privmsg(target,self.bot.emo(msg))
#######################################################################################
#######################################################################################
@command(permission='view')
def dsa_hardchatters(self, mask, target, args):
"""dsa_hardchatters
%%dsa_hardchatters
"""
try:
OP_FLAG=False
channel = self.bot.channels[target]
for _, nick in channel.modes.items():
for __ in _:
modes=["%","@","&","~"]
if __ in modes:
if mask.nick in nick:
OP_FLAG=True
if OP_FLAG:
if self.bot.dbio.hardchatters:
_msg = re.findall(r'.{1,400}(?:\s+|$)', f'{B}hardchatters: {X}{self.bot.dbio.hardchatters}')
for _ in _msg:
self.bot.privmsg(target,self.bot.emo(_))
else:
self.bot.privmsg(target,self.bot.emo(f"{R}no hardchatters"))
else:
return
except Exception as e:
msg=f'error: dsa_plugin:hardchatters > {e}'
self.bot.privmsg(target,self.bot.emo(msg)); print(msg)
#######################################################################################
#######################################################################################
@command(permission='view')
def dsa_lurkers(self, mask, target, args):
"""dsa_lurkers
%%dsa_lurkers
"""
try:
OP_FLAG=False
channel = self.bot.channels[target]
for _, nick in channel.modes.items():
for __ in _:
modes=["%","@","&","~"]
if __ in modes:
if mask.nick in nick:
OP_FLAG=True
if OP_FLAG:
if self.bot.dbio.lurkers:
_msg = re.findall(r'.{1,400}(?:\s+|$)', f'{B}lurkers: {X}{self.bot.dbio.lurkers}')
for _ in _msg:
self.bot.privmsg(target, self.bot.emo(_))
else:
self.bot.privmsg(target, self.bot.emo(f"{R}no lurkers"))
###########################################################################
except Exception as e:
msg=f'error: dsa_plugin:lurkers > {e}'
self.bot.privmsg(target,self.bot.emo(msg)); print(msg)
#######################################################################################
#######################################################################################
@command(permission='admin')
def dsa_analyze(self,mask,target,args):
"""dsa_analyze
%%dsa_analyze <message>...
"""
data=''.join(args['<message>']).strip()
###################################################################################
db_info=self.bot.config['storage']
base_dir=f"{os.getcwd()}/{db_info.split('/')[-2]}"
db_pathname=f"{base_dir}/{db_info.split('/')[-1]}"
f=open(db_pathname,'r');l=f.read();f.close()
###################################################################################
db=l.splitlines()
for _ in db:
if not _.find('last_msg_for_')==-1:
self.bot.dbio.seen_users.append(_.split('last_msg_for_')[1].split('"')[0])
###################################################################################
nicks=[]; key=''; USER=data.lower(); SEARCH_FLAG=False
###################################################################################
try:
key=self.bot.db.getlist(f'last_msg_for_{USER}')
except Exception as e:
print(f'error: dsa:nicks -> pulling db key for last_msg_for: {e}')
if not key: SEARCH_FLAG=True
###################################################################################
self.bot.dbio.search_users=[]
IS_RUNNING=True
###################################################################################
LOOP=1
if SEARCH_FLAG:
for _ in db:
if not _.find('last_msg_for_')==-1:
if not _.find(USER)==-1:
self.bot.dbio.search_users.append(_.split('last_msg_for_')[-1].split('":')[0])
LOOP=len(self.bot.dbio.search_users)
###################################################################################
for i in range(0,LOOP):
if SEARCH_FLAG:
try:
USER=self.bot.dbio.search_users[i]
key=self.bot.db.getlist(f'last_msg_for_{USER}')
except:
IS_RUNNING=False
msg=f"{B}< {R}no {G}{USER} {R}ircnick(s) or similar were found {B}>"
self.bot.privmsg(target,msg)
###############################################################################
newnicks=0; privmsgs=0; lastmsg=[]; lasttime=[]
###############################################################################
try:
for _ in key:
if _['type']=='newnick':
newnicks+=1
elif _['type']=='privmsg':
privmsgs+=1
lastmsg.append(_['msg'])
lasttime.append(_['time'])
except Exception as e:
print(f'error: dsa:nicks:search -> no items of a key means no db operation: {e}')
###############################################################################
self.log(f'{"#"*69}')
###############################################################################
if newnicks>0 or privmsgs>0:
self.log(f"{D}<<< {R}{USER} {G}- {R}ircnicks{B}: {R}{int(newnicks)+1} {G}- {R}messages{B}: {R}{privmsgs} {D}>>>")
###############################################################################
if privmsgs>0:
for i in range(0,privmsgs):
self.log(f'{"#"*69}')
self.log(f'{D}{lasttime[i]} {B}> {R}{USER} {B}> {D}{lastmsg[i]}')
###############################################################################
if newnicks==0:
self.log(f"!!! no alternate ircnicks and not enough data -- softchats !!!")
self.log_play(target)
if i==0 and not SEARCH_FLAG:
IS_RUNNING=False
return
elif i==LOOP and SEARCH_FLAG:
IS_RUNNING=False
return
###############################################################################
self.log(f'{"#"*69}')
###############################################################################
while IS_RUNNING:
###########################################################################
if not SEARCH_FLAG:
msg=f"{D}<<< {R}near region{D} >>>"
else:
msg=f"{D}<<< {R}near region - issues{D} >>>"
self.log(msg); self.log(f'{"#"*69}')
###########################################################################
for _ in key:
if _['type']=='newnick':
f,t=_['msg'].split(' changed nick to ')
self.log(f'{D}{_["time"]} {B}> {R}from ircnick {G}{f} {R}to ircnick {G}{t}')
nicks.append(f)
###########################################################################
nicks=list(set(nicks))
###########################################################################
self.log(f'{"#"*69}')
###########################################################################
if not SEARCH_FLAG:
msg=f'{D}[ {G}{USER} {D}- {R}alternate ircnicks - direct referfence{D}]{B}: {R}{" ".join(nicks)}'
else:
msg=f'{D}[ {G}{USER} {D}- {R}alternate ircnicks - keyworded{D}]{B}: {R}{" ".join(nicks)}'
###########################################################################
self.log(msg); self.log(f'{"#"*69}'); self.log(f"{D}<<< {R}distant region{D} >>>"); self.log(f'{"#"*69}')
for _ in nicks:
self.log(f'[{_}]')
try:
key=self.bot.db.getlist(f'last_msg_for_{_}')
for __ in key:
###############################################################
if __['type']=='privmsg':
self.log(f'\t{D}{__["time"]} {B}> {R}{_} {B}> {D}{__["msg"]}"')
###############################################################
elif __['type']=='newnick':
f,t=__['msg'].split(' changed nick to ')
self.log(f'\t{D}{__["time"]} {B}> {R}to ircnick {B}{t} {R}from ircnick {G}{f}')
oldkey=""
IS_OK=True
while IS_OK==True:
try:
keysub=self.bot.db.getlist(f'last_msg_for_{f}')
if oldkey==keysub:
break
oldkey=keysub
for ___ in keysub:
if ___['type']=='newnick':
f,t=___['msg'].split(' changed nick to ')
self.log(f'\t{D}{___["time"]} {B}> {R}to ircnick {G}{t} {R}from ircnick {G}{f}"')
elif __['type']=='privmsg':
self.log(f'\t{D}{__["time"]} {B}> {R}{_} {B}> {D}{__["msg"]}"')
except Exception as e:
IS_OK=False
print(f'error: dsa:dirties:IS_OK -> pulling db key for last_msg_for_{f}: {e}')
###############################################################
except Exception as e:
self.log(f'\t{D}<{R}none{D}>')
print(f'error: dsa:dirties -> pulling db key for last_msg_for_{_}: {e}')
###########################################################################
if SEARCH_FLAG:
self.log(f'{B}<<< {R}total instances found{X}: {R}{LOOP} {B}>>>')
self.log_play(target)
if SEARCH_FLAG:
try:
self.search_users.reverse()
self.pop()
self.search_users.reverse()
except:
IS_RUNNING=False
else:
IS_RUNNING=False
#######################################################################################
#######################################################################################
@command(permission='admin')
@irc3.extend
def dsa_scan(self,mask,target,args):
"""tool to scan and designate
%%dsa_scan
"""
OP_FLAG=False
channel=self.bot.channels[target]
if not mask.nick==self.bot.nick:
for _, nick in channel.modes.items():
for __ in _:
modes=["%","@","&","~"]
if __ in modes:
if mask.nick in nick:
OP_FLAG=True
else:
OP_FLAG=True
if OP_FLAG:
# msg=f'{B}<<< {X}analyzing {target} users for lurkers {B}>>>'
# self.log(msg)
msg=f'<<< analyzing {target} users for lurkers >>>'
print(msg)
###############################################################################
###############################################################################
if not target in self.bot.dbio.channel:
self.bot.dbio.channel.append(target)
self.bot.dbio.channel_users.append(channel)
else:
index=self.bot.dbio.channel.index(target)
self.bot.dbio.channel_users[index]=self.bot.channels[target]
###############################################################################
###############################################################################
for _ in channel:
USER=_.lower()
try:
key=self.bot.db.getlist(f'last_msg_for_{USER}')
if key==None:
if USER in self.bot.dbio.negated:
if not USER in self.bot.dbio.hardchatters:
self.dbentry_add('hardchatters',USER)
# msg=f'{B}{USER} {B}>>> {X}whitelisted user {G}- {X}negated'
# self.log(msg)
msg=f'{USER} >>> whitelisted user - negated'
print(msg)
else:
# msg=f'already in db - {B}{USER} {B}>>> {X}whitelisted user {G}- {X}negated'
# self.log(msg)
msg=f'already in db - {USER} >>> whitelisted user - negated'
print(msg)
else:
if not USER in self.bot.dbio.lurkers:
self.dbentry_add('lurkers',USER)
#msg=f'{G}{USER} {S}>>> {G}- {S}given a lurker status - no record'
#self.log(msg)
msg=f'{USER} >>> - given a lurker status - no record'
print(msg)
else:
print(f'already in db - {G}{USER} {S}>>> {S}lurker')
cmd=f"MODE {target} -v {USER}"
self.bot.send(cmd)
else:
###################################################################
###################################################################
records=len(key)
records_timestamps=[]
records_privmsgs=[]
records_newnicks=[]
for _key in key:
timestamp=self.epochdelta(_key['time'])
records_timestamps.append(_key['time'])
if _key['type']=='privmsg':
records_privmsgs.append(timestamp)
else:
records_newnicks.append(timestamp)
###################################################################
###################################################################
records_timestamps.sort()
idle_timestamp=self.epochtimestamp(records_timestamps[-1])
newest_timestamp=self.epochdelta(records_timestamps[-1])
records_timestamps.reverse()
oldest_timestamp=self.epochdelta(records_timestamps[-1])
oldest_timestamp_epoch=self.epochtimestamp(records_timestamps[-1])
history_timestamp=datetime.now().timestamp()-oldest_timestamp_epoch
###################################################################
###################################################################
idle_status=False
lurker_status=False
history_status=False
hardchat_status=False
idle_threshold=24*(60*60)
idle_time=datetime.now().timestamp()-idle_timestamp
if history_timestamp>=self.bot.dbio.threshold:
history_status=True
if idle_time >= idle_threshold:
idle_status=True
if USER in self.bot.dbio.hardchatters:
hardchat_status=True
if USER in self.bot.dbio.lurkers:
lurker_status=True
###################################################################
###################################################################
if records_privmsgs==0:
self.dbentry_add('lurkers',USER)
lurker_status=True
#msg=f'{B}{USER} {X}>>> {R}interactions: {records} - {X}last time spoken: never - {R}oldest/newest interaction: {oldest_timestamp}{B}/{R}{newest_timestamp} - {X}idle: {idle_status} - {R}hardchatter: {hardchat_status} - {X}lurker: {lurker_status}'
#self.log(msg)
msg=f'{USER} >>> interactions: {records} - last time spoken: never - oldest/newest interaction: {oldest_timestamp}/{newest_timestamp} - idle: {idle_status} - hardchatter: {hardchat_status} - lurker: {lurker_status}'
print(msg)
else:
try:
if USER in self.bot.dbio.lurkers:
if not USER in self.bot.dbio.hardchatters:
#msg=f'{G}{USER} {B}- {S}has a lurker status'
#self.log(msg)
msg=f'{USER} - has a lurker status'
print(msg)
cmd=f"MODE {target} -v {USER}"
self.bot.send(cmd)
if USER in self.bot.dbio.hardchatters:
#msg=f'{G}{USER} {B}- {S}has a lurker status yet a hardchatter'
#self.log(msg)
msg=f'{USER} - has a lurker status yet a hardchatter'
print(msg)
cmd=f"MODE {target} +v {USER}"
self.bot.send(cmd)
else:
if not USER in self.bot.dbio.hardchatters:
self.dbentry_add('hardchatters',USER)
cmd=f"MODE {target} +v {USER}"
self.bot.send(cmd)
#msg=f'{B}{USER} {X}>>> {R}interactions: {records} - {X}last time spoken: {records_privmsgs[-1]} - {R}oldest/newest interaction: {oldest_timestamp}{B}/{R}{newest_timestamp} - {X}idle: {idle_status} - {R}hardchatter: {hardchat_status} - {X}lurker: {lurker_status} - {R}history: {history_status}'
msg=f'{USER} >>> interactions: {records} - last time spoken: {records_privmsgs[-1]} - oldest/newest interaction: {oldest_timestamp}/{newest_timestamp} - idle: {idle_status} - hardchatter: {hardchat_status} - lurker: {lurker_status} - history: {history_status}'
print(msg)
except Exception as e:
msg=f'error: dsa_plugin:hardchattting > {e}'
print(msg); self.bot.privmsg(target,self.bot.emo(msg))
#######################################################################
#######################################################################
except Exception as e:
print(f'error: dsa:lurkers > {e}')
###############################################################################
###############################################################################
self.log_play(target)
#######################################################################################
#######################################################################################
@command(permission='admin')
def dsa_set_threshold(self,mask,target,args):
"""dsa_set_threshold - sets a threshold on distance in days - if there is no record within that amount of time then they fall below the automated system. default value is 30 days. 90 day threshold example: ?dsa_set_threshold 90
%%dsa_set_threshold <message>...
"""
###################################################################################
###################################################################################
data=''.join(args['<message>']).strip()
###################################################################################
###################################################################################
try:
self.bot.dbio.threshold=int(data)*24*(60*60)
msg=f'{R}{mask.nick}{B}: {R}dsa_plugin:dsa_threshold set to {B}{data} days'
except Exception as e:
msg=f'{mask.nick}: error - dsa_plugin:dsa_threshold > {e}'
print(msg); self.bot.privmsg(target,self.bot.emo(msg))
#######################################################################################
#######################################################################################
#### EOF CLASS INSTANCE EOF CLASS INSTANCE EOF CLASS INSTANCE EOF CLASS INSTANCE
#### EOF CLASS INSTANCE EOF CLASS INSTANCE EOF CLASS INSTANCE EOF CLASS INSTANCE
###########################################################################################
###########################################################################################
@cron('*/5 * * * *')
def _dsa_scanning(bot):
for channel in bot.channels:
args=""
try:
print('cronjob: auto-dsa_scanning')
bot.dsa_scan(bot.nick,channel,args)
except:
print('error - cronjob: auto-dsa_scanning')
####################################################################################### EOF
####################################################################################### EOF