import argparse
import datetime
import os.path
import re
import subprocess
import sys
import time
import urllib
import urllib.request
from urllib.error import URLError, HTTPError
from lxml import etree
from pushbullet import PushBullet
def initial_arg():
argparser = argparse.ArgumentParser(description='nowere.py --param')
argparser.add_argument('--notif', help='Notify about new posts after last visit only', action='store_true')
argparser.add_argument('--text-only', help='Send notification with post text only', action='store_true')
argparser.add_argument('--text-image', help='Send notification with post text and image', action='store_true')
start_arg = None
args, unknown = argparser.parse_known_args()
if len(unknown) != 0: # еррор кондишион и выход, непонятные арги
print(f'Unknown args {unknown}')
argparser.print_help(sys.stderr)
sys.exit(1)
if (len(sys.argv)) == 2: # Проверка на один единственный арг, первый само имя скрипта
for arg in vars(args): # перебор и поиск введенного арга
if getattr(args, arg):
start_arg = arg
print(f'Starting arg is --{start_arg}')
break
else:
start_arg = 'text_only'
print(
f'Starting arg is --{start_arg}') # сработает без аргументов, дефолт значение, либо выдавать инфо и выходить, нижние две строки
# argparser.print_help(sys.stderr)
# sys.exit(1)
del argparser, args, unknown
return start_arg
def parse_post(number, text):
if Last_reported < number:
root = etree.HTML(text)
path = root.find('.//td[@id="reply%d"]' % number)
OP_post_tag = 0
if path is None:
OP_post_tag = 1
image_url = None
blockquote_mark = 0
post_text = str()
picture_path = None
if OP_post_tag == 1:
path = root.find('.//form[@id="delform"]')
for i in path.iter():
if i.attrib.get('class') == 'filetitle':
thread_name = i.text
post_text = post_text + f'New thread started, title : {thread_name}\n'
elif i.attrib.get('target') == '_blank':
if start_arg == 'text_image':
if image_url is None:
image_url = f'https://nowere.net{i.attrib.get("href")}'
image_name = image_url[-17:]
picture_request = urllib.request.urlretrieve(f'{image_url}', f'pics/{image_name}')
picture_path = f'pics/{image_name}'
post_text = post_text + f'Picture attached with post, name: {image_name}\n'
else:
continue
else:
continue
elif i.attrib.get('class') == 'omittedposts':
break
elif i.tag == 'table':
break
elif i.attrib.get('clear') == 'left':
break
elif i.tag == 'blockquote':
blockquote_mark = 1
if blockquote_mark == 1:
# TODO Comment is too long
if i.tag == 'div':
blockquote_mark = 0
post_text = post_text + 'Comment is too long for this version of the script.\n'
continue
# TODO Comment is too long
if i.text is not None:
post_text = post_text + f'{i.text}\n'
if i.tail:
i.tail = i.tail.strip('\n')
i.tail = i.tail.strip()
if i.tail:
post_text = post_text + f'{i.tail}\n'
return str(post_text), picture_path
else:
return None, None
def tcp_loop():
tcp_loop_state = True
# ------------------------------------------
tcp_viewed = False
while tcp_loop_state:
# first iteration always produces empty byte string in non-blocking mode
line = p.stdout.readline()
if line != b'':
visit_time_string = re.search(r'\d{4}-\d{2}-\d{2} \d{1,2}:\d{1,2}:\d{1,2}\.\d*',
line.decode('utf-8')).group(0)
visit_time = datetime.datetime.strptime(visit_time_string, '%Y-%m-%d %H:%M:%S.%f')
# visit_time_list = list(map(int,re.split(':', visit_time_string)))
# visit_time = visit_time_list[0] * 3600 + visit_time_list[1] * 60 + visit_time_list[2]
if not disable_time_start < visit_time < disable_time_end:
if not tcp_viewed:
print(visit_time_string)
tcp_viewed = True
# print(visit_time_string)
else:
tcp_loop_state = False
return tcp_viewed
def send_notification(post_text, picture):
if start_arg == 'notif':
post_text = str(New_posts) + ' new posts'
push = pb.push_note(data, post_text)
elif start_arg == 'text_only':
push = pb.push_note(data, post_text)
elif start_arg == 'text_image':
if picture is not None:
with open(f'{picture}', "rb") as pic:
file_data = pb.upload_file(pic, f'{picture[5:]}')
push_picture = pb.push_file(**file_data)
push = pb.push_note(data, post_text)
else:
push = pb.push_note(data, post_text)
delay_period_sec = 10
Message_period_sec = 60
link = "http://nowere.net/b"
file_name = "Data_Post_No.dat"
access_token = ""
command = """addr=$(host nowere.net | awk '/has address/ {print$4}');
addr="dst ""${addr}"" and (port 80 or port 443)";
tcpdump -i tun0 -tttt -nn "${addr}" -l """
start_arg = initial_arg()
try:
file = open(file_name, 'r')
Last_viewed = int(re.search(r'Last_viewed = (\d+)', file.readline()).group(1))
Status = re.search(r'Status = "(down|up)"', file.readline()).group(1)
except Exception as e:
with open(file_name, 'w') as file:
file.write('Last_viewed = 0\nStatus = "down"\n')
Last_viewed = 0
Status = 'down'
pb = PushBullet(access_token)
data = 'Nowere'
text = ''
print('Last_viewed = ', Last_viewed)
print('Status = "' + Status + '"')
Last_reported = Max_No = Last_viewed
flag_reset_tcpdump = 0
pre_utc_dt = datetime.datetime.utcnow()
p = subprocess.Popen(command, shell=True, bufsize=-1, stdout=subprocess.PIPE)
os.set_blocking(p.stdout.fileno(), False)
start = time.time()
delay = chan_status_notified = 0
while True:
# ------------------------------------------
if time.time() > start + delay:
disable_time_start = datetime.datetime.now()
int_time_start = int(time.time())
request_time = datetime.datetime.utcnow() # debug
try:
f = urllib.request.urlopen(link) # может иметь таймаут вплоть до 30 секунд, 10 не подойдёт, начнётся дос
print(f'Request {request_time}') # debug
except HTTPError as e:
# Error_flag = True
Error_text = 'The server couldn\'t fulfill the request.\nError code: ' + str(e.code)
Status = "down"
except URLError as e:
# Error_flag = True
Error_text = 'We failed to reach a server.\nReason: ' + str(e.reason)
Status = "down"
else:
site = f.read().decode('utf-8')
# last_post = re.findall(r'<span .*<a .*No\.(\d+)</a>.*</span>', site)
last_post = re.findall(r'<span class="reflink"> <a href=".{0,100}">No\.(\d+)</a> </span>', site)
Max_No = max(list(map(int, last_post)))
Status = "up"
if Status == 'up':
picture_send = text_send = False
post_text, picture = parse_post(Max_No, site)
if picture is not None:
picture_send = True
if post_text is not None:
text_send = True
print("Max No ", Max_No)
disable_time_end = datetime.datetime.now()
int_time_end = int(time.time())
delay += (int_time_end - int_time_start + delay_period_sec)
tcp_viewed = tcp_loop()
if tcp_viewed:
if Last_viewed < Max_No:
Last_viewed = Max_No
with open(file_name, 'w') as file:
file.write(f'Last_viewed = {Last_viewed}\nStatus = "{Status}"\n')
if chan_status_notified == 1 and Status == 'up':
chan_status_notified = 0
print('chan is UP again!')
text = 'chan is UP again!'
push = pb.push_note(data, text)
elif Status == 'down':
# Send_Message('chan is DOWN!\n' + Error_text)
if chan_status_notified == 0:
print('chan is down!\n' + Error_text)
text = 'chan is down!\n' + Error_text
push = pb.push_note(data, text)
chan_status_notified = 1
continue
if Last_viewed < Max_No:
if Last_reported < Max_No:
New_posts = Max_No - Last_viewed
print(str(New_posts) + ' new posts')
if text_send or picture_send:
send_notification(post_text, picture)
Last_reported = Max_No
with open(file_name, 'w') as file:
file.write(f'Last_viewed = {Last_viewed}\nStatus = "{Status}"\n')
# ------------------------------------------
utc_dt = datetime.datetime.utcnow()
if (utc_dt.hour == 21) and (pre_utc_dt.hour == 20):
p.terminate()
p = subprocess.Popen(command, shell=True, bufsize=-1, stdout=subprocess.PIPE)
os.set_blocking(p.stdout.fileno(), False)
print('TCPDUMP reset')
push = pb.push_note(data, 'TCPDUMP reset')
pre_utc_dt = utc_dt
time.sleep(0.5)
p.terminate()