This commit is contained in:
Schimon Jehudah 2022-09-24 19:01:57 +00:00
parent 0c32f95814
commit 2e23e93584

View file

@ -1,46 +1,31 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" import os
Created on Sun May 15 17:09:05 2022
@author: Schimon Jehudah, Adv.
"""
from argparse import ArgumentParser from argparse import ArgumentParser
from asyncio.exceptions import IncompleteReadError from asyncio.exceptions import IncompleteReadError
from bs4 import BeautifulSoup
from datetime import date from datetime import date
from getpass import getpass from getpass import getpass
from http.client import IncompleteRead from http.client import IncompleteRead
from os import path
from urllib import error from urllib import error
#from urllib.parse import urlparse
#from xdg import BaseDirectory
import aiohttp
import asyncio import asyncio
import feedparser
import logging import logging
import os
import os.path
import slixmpp
import sqlite3 import sqlite3
from sqlite3 import Error from sqlite3 import Error
import sys import sys
import time
#import xdg
# offline = False import aiohttp
from bs4 import BeautifulSoup
import feedparser
import slixmpp
DBLOCK = asyncio.Lock()
class Slixfeed(slixmpp.ClientXMPP): class Slixfeed(slixmpp.ClientXMPP):
""" """
Slixmpp bot that will send updates of feeds it Slixmpp bot that will send updates of feeds it
receives. receives.
""" """
lock = asyncio.Lock()
print("locked?")
print(lock.locked())
def __init__(self, jid, password): def __init__(self, jid, password):
slixmpp.ClientXMPP.__init__(self, jid, password) slixmpp.ClientXMPP.__init__(self, jid, password)
@ -51,6 +36,7 @@ class Slixfeed(slixmpp.ClientXMPP):
# our roster. # our roster.
self.add_event_handler("session_start", self.start) self.add_event_handler("session_start", self.start)
self.add_event_handler("session_start", self.send_updates) self.add_event_handler("session_start", self.send_updates)
self.add_event_handler("session_start", self.check_updates)
# The message event is triggered whenever a message # The message event is triggered whenever a message
# stanza is received. Be aware that that includes # stanza is received. Be aware that that includes
@ -58,14 +44,6 @@ class Slixfeed(slixmpp.ClientXMPP):
self.add_event_handler("message", self.message) self.add_event_handler("message", self.message)
self.add_event_handler("disconnected", self.reconnect) self.add_event_handler("disconnected", self.reconnect)
# async def reconnect(self, event):
# await asyncio.sleep(10)
# offline = True
# print(time.strftime("%H:%M:%S"))
# print(offline)
# self.connect()
# #return True
async def start(self, event): async def start(self, event):
""" """
Process the session_start event. Process the session_start event.
@ -104,65 +82,59 @@ class Slixfeed(slixmpp.ClientXMPP):
elif message.lower().startswith('feed recent '): elif message.lower().startswith('feed recent '):
print("COMMAND: feed recent") print("COMMAND: feed recent")
print("ACCOUNT: " + str(msg['from'])) print("ACCOUNT: " + str(msg['from']))
action = await initdb(msg['from'].bare, action = await initdb(msg['from'].bare, last_entries, message[12:])
False,
message[12:],
last_entries)
elif message.lower().startswith('feed search '): elif message.lower().startswith('feed search '):
print("COMMAND: feed search") print("COMMAND: feed search")
print("ACCOUNT: " + str(msg['from'])) print("ACCOUNT: " + str(msg['from']))
action = await initdb(msg['from'].bare, action = await initdb( msg['from'].bare, search_entries, message[12:])
False,
message[12:],
search_entries)
elif message.lower().startswith('feed list'): elif message.lower().startswith('feed list'):
print("COMMAND: feed list") print("COMMAND: feed list")
print("ACCOUNT: " + str(msg['from'])) print("ACCOUNT: " + str(msg['from']))
action = await initdb(msg['from'].bare, action = await initdb(msg['from'].bare, list_subscriptions)
False,
False,
list_subscriptions)
elif message.lower().startswith('feed add '): elif message.lower().startswith('feed add '):
print("COMMAND: feed add") print("COMMAND: feed add")
print("ACCOUNT: " + str(msg['from'])) print("ACCOUNT: " + str(msg['from']))
action = await initdb(msg['from'].bare, action = await initdb(msg['from'].bare, add_feed, message[9:])
self.lock,
message[9:],
add_feed)
elif message.lower().startswith('feed remove '): elif message.lower().startswith('feed remove '):
print("COMMAND: feed remove") print("COMMAND: feed remove")
print("ACCOUNT: " + str(msg['from'])) print("ACCOUNT: " + str(msg['from']))
action = await initdb(msg['from'].bare, action = await initdb(msg['from'].bare, remove_feed, message[12:])
self.lock,
message[12:],
remove_feed)
elif message.lower().startswith('feed status '): elif message.lower().startswith('feed status '):
print("COMMAND: feed status") print("COMMAND: feed status")
print("ACCOUNT: " + str(msg['from'])) print("ACCOUNT: " + str(msg['from']))
action = await initdb(msg['from'].bare, action = await initdb(msg['from'].bare, toggle_status, message[12:])
self.lock,
message[12:],
toggle_status)
elif message.lower().startswith('enable'): elif message.lower().startswith('enable'):
print("COMMAND: enable") print("COMMAND: enable")
print("ACCOUNT: " + str(msg['from'])) print("ACCOUNT: " + str(msg['from']))
action = toggle_state(msg['from'].bare, action = toggle_state(msg['from'].bare, True)
True)
elif message.lower().startswith('disable'): elif message.lower().startswith('disable'):
print("COMMAND: disable") print("COMMAND: disable")
print("ACCOUNT: " + str(msg['from'])) print("ACCOUNT: " + str(msg['from']))
action = toggle_state(msg['from'].bare, action = toggle_state(msg['from'].bare, False)
False)
else: else:
action = "Unknown command. Press \"help\" for list of commands" action = 'Unknown command. Press "help" for list of commands'
msg.reply(action).send() msg.reply(action).send()
async def send_updates(self, event): async def check_updates(self, event):
#while not offline: while True:
print("Checking update")
db_dir = get_default_dbdir()
if not os.path.isdir(db_dir):
msg = ("Slixfeed can not work without a database. \n"
"To create a database, follow these steps: \n"
"Add Slixfeed contact to your roster \n"
"Send a feed to the bot by: \n"
"feed add https://reclaimthenet.org/feed/")
print(msg)
else:
files = os.listdir(db_dir)
for file in files:
jid = file[:-3]
await initdb(jid, download_updates)
await asyncio.sleep(9)
async def send_updates(self, event):
while True: while True:
#async with self.lock:
print(time.strftime("%H:%M:%S"))
# print(offline)
db_dir = get_default_dbdir() db_dir = get_default_dbdir()
if not os.path.isdir(db_dir): if not os.path.isdir(db_dir):
msg = ("Slixfeed can not work without a database. \n" msg = ("Slixfeed can not work without a database. \n"
@ -177,71 +149,19 @@ class Slixfeed(slixmpp.ClientXMPP):
for file in files: for file in files:
if not file.endswith('.db-jour.db'): if not file.endswith('.db-jour.db'):
jid = file[:-3] jid = file[:-3]
# TODO check if jid online new = await initdb(
# https://slixmpp.readthedocs.io/en/latest/api/plugins/xep_0199.html jid,
# d = self.send_ping(self, jid) get_unread
# print('d') )
# print(d)
new = await initdb(jid, self.lock, False, get_unread)
if new: if new:
msg = self.make_message(mto=jid, mbody=new, msg = self.make_message(
mtype='chat') mto=jid,
mbody=new,
mtype='chat'
)
msg.send() msg.send()
# today = str(date.today())
# news.insert = [0, 'News fetched on: ' + today]
#news.append('End of News update')
#for new in news:
#print("sending to: jid")
#print("sending to: " + jid)
# self.send_message(mto=jid,
# mbody=new,
# mtype='normal').send()
#msg = self.make_message(mto=jid,
# mbody=new,
# mtype='chat')
#print(msg)
#msg.send()
await asyncio.sleep(60 * 3) await asyncio.sleep(60 * 3)
# asyncio.ensure_future(send_updates(self))
async def check_updates():
#async with Slixfeed.lock:
while True:
db_dir = get_default_dbdir()
if not os.path.isdir(db_dir):
msg = ("Slixfeed can not work without a database. \n"
"To create a database, follow these steps: \n"
"Add Slixfeed contact to your roster \n"
"Send a feed to the bot by: \n"
"feed add https://reclaimthenet.org/feed/")
print(msg)
else:
os.chdir(db_dir)
files = os.listdir()
for file in files:
jid = file[:-3]
await initdb(
jid,
Slixfeed.lock,
False,
download_updates
)
await asyncio.sleep(60 * 30)
asyncio.ensure_future(check_updates())
# async def tasks():
# # Begin scanning feeds
# task = asyncio.create_task(check_updates())
# await task
async def tasks(jid, password):
# Begin scanning feeds
await asyncio.gather(
check_updates(),
Slixfeed(jid, password).send_updates()
)
def print_help(): def print_help():
msg = ("Slixfeed - News syndication bot for Jabber/XMPP \n" msg = ("Slixfeed - News syndication bot for Jabber/XMPP \n"
@ -280,6 +200,7 @@ def print_help():
" https://pythonhosted.org/feedparser") " https://pythonhosted.org/feedparser")
return msg return msg
# Function from buku # Function from buku
# https://github.com/jarun/buku # https://github.com/jarun/buku
# Arun Prakash Jana (jarun) # Arun Prakash Jana (jarun)
@ -311,15 +232,24 @@ def get_default_dbdir():
data_home = os.path.join(os.environ.get('HOME'), '.local', 'share') data_home = os.path.join(os.environ.get('HOME'), '.local', 'share')
return os.path.join(data_home, 'slixfeed') return os.path.join(data_home, 'slixfeed')
# TODO Perhaps this needs to be executed # TODO Perhaps this needs to be executed
# just once per program execution # just once per program execution
async def initdb(jid, lock, message, callback): async def initdb(jid, callback, message=None):
print("initdb(jid, lock, message, callback)")
db_dir = get_default_dbdir() db_dir = get_default_dbdir()
if not os.path.isdir(db_dir): if not os.path.isdir(db_dir):
os.mkdir(db_dir) os.mkdir(db_dir)
os.chdir(db_dir) db_file = os.path.join(db_dir, r"{}.db".format(jid))
db_file = r"{}.db".format(jid) create_tables(db_file)
if message:
return await callback(db_file, message)
else:
return await callback(db_file)
def create_tables(db_file):
with create_connection(db_file) as conn:
feeds_table_sql = """ feeds_table_sql = """
CREATE TABLE IF NOT EXISTS feeds ( CREATE TABLE IF NOT EXISTS feeds (
id integer PRIMARY KEY, id integer PRIMARY KEY,
@ -340,30 +270,11 @@ async def initdb(jid, lock, message, callback):
source text, source text,
read integer read integer
); """ ); """
# create a database connection
conn = create_connection(db_file) c = conn.cursor()
# create tables c.execute(feeds_table_sql)
if conn is not None: c.execute(entries_table_sql)
# create projects table
create_table(conn, feeds_table_sql)
create_table(conn, entries_table_sql)
else:
print("Error! cannot create the database connection.")
if lock:
if message:
print("if message")
print(message)
print(lock.locked())
return await callback(conn, lock, message)
else:
print("if message else")
print(lock.locked())
return await callback(conn, lock)
elif message:
return await callback(conn, message)
else:
print("lock else")
return await callback(conn)
def create_connection(db_file): def create_connection(db_file):
""" """
@ -380,38 +291,21 @@ def create_connection(db_file):
print(e) print(e)
return conn return conn
def create_table(conn, create_table_sql):
"""
Create a table from the create_table_sql statement
:param conn: Connection object
:param create_table_sql: a CREATE TABLE statement
:return:
"""
try:
c = conn.cursor()
print(time.strftime("%H:%M:%S"), "conn.cursor() from create_table(conn, create_table_sql)")
c.execute(create_table_sql)
except Error as e:
print(e)
# def setup_info(jid): async def download_updates(db_file):
# def start_process(jid): with create_connection(db_file) as conn:
async def download_updates(conn, lock):
async with lock:
print("download_updates(conn, lock)")
with conn:
# cur = conn.cursor()
# get current date
#today = date.today()
urls = await get_subscriptions(conn) urls = await get_subscriptions(conn)
for url in urls: for url in urls:
#"".join(url)
source = url[0] source = url[0]
res = await download_feed(conn, source) res = await download_feed(source)
cur = conn.cursor()
sql = "UPDATE feeds SET status = :status, scanned = :scanned WHERE address = :url" sql = "UPDATE feeds SET status = :status, scanned = :scanned WHERE address = :url"
async with DBLOCK:
with create_connection(db_file) as conn:
cur = conn.cursor()
cur.execute(sql, {"status": res[1], "scanned": date.today(), "url": source}) cur.execute(sql, {"status": res[1], "scanned": date.today(), "url": source})
conn.commit()
if res[0]: if res[0]:
try: try:
feed = feedparser.parse(res[0]) feed = feedparser.parse(res[0])
@ -425,8 +319,10 @@ async def download_updates(conn, lock):
else: else:
valid = 1 valid = 1
sql = "UPDATE feeds SET valid = :validity WHERE address = :url" sql = "UPDATE feeds SET valid = :validity WHERE address = :url"
async with DBLOCK:
with create_connection(db_file) as conn:
cur = conn.cursor()
cur.execute(sql, {"validity": valid, "url": source}) cur.execute(sql, {"validity": valid, "url": source})
conn.commit()
except (IncompleteReadError, IncompleteRead, error.URLError) as e: except (IncompleteReadError, IncompleteRead, error.URLError) as e:
print(e) print(e)
return return
@ -434,14 +330,19 @@ async def download_updates(conn, lock):
# NOTE Need to correct the SQL statement to do so # NOTE Need to correct the SQL statement to do so
entries = feed.entries entries = feed.entries
length = len(entries) length = len(entries)
async with DBLOCK:
with create_connection(db_file) as conn:
await remove_entry(conn, source, length) await remove_entry(conn, source, length)
for entry in entries: for entry in entries:
if entry.has_key("title"): if entry.has_key("title"):
title = entry.title title = entry.title
else: else:
title = feed["feed"]["title"] title = feed["feed"]["title"]
link = source if not entry.link else entry.link link = source if not entry.link else entry.link
with create_connection(db_file) as conn:
exist = await check_entry(conn, title, link) exist = await check_entry(conn, title, link)
if not exist: if not exist:
if entry.has_key("summary"): if entry.has_key("summary"):
summary = entry.summary summary = entry.summary
@ -453,16 +354,13 @@ async def download_updates(conn, lock):
summary = '*** No summary ***' summary = '*** No summary ***'
#print('~~~~~~summary not in entry') #print('~~~~~~summary not in entry')
entry = (title, summary, link, source, 0); entry = (title, summary, link, source, 0);
async with DBLOCK:
with create_connection(db_file) as conn:
await add_entry(conn, entry) await add_entry(conn, entry)
await set_date(conn, source) await set_date(conn, source)
#make_message
# message = title + '\n\n' + summary + '\n\nLink: ' + link
# print(message)
# news.append(message)
# print(len(news))
# return news
async def download_feed(conn, url):
async def download_feed(url):
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
async with session.get(url) as response: async with session.get(url) as response:
status = response.status status = response.status
@ -472,10 +370,8 @@ async def download_feed(conn, url):
else: else:
return [False, status] return [False, status]
loop = asyncio.get_event_loop()
loop.run_until_complete
async def check_feed(conn, lock, url): async def check_feed(conn, url):
""" """
Check whether a feed exists Check whether a feed exists
Query for feeds by url Query for feeds by url
@ -483,29 +379,32 @@ async def check_feed(conn, lock, url):
:param url: :param url:
:return: row :return: row
""" """
async with lock:
cur = conn.cursor() cur = conn.cursor()
print(time.strftime("%H:%M:%S"), "conn.cursor() from check_feed(conn, url)")
sql = "SELECT id FROM feeds WHERE address = ?" sql = "SELECT id FROM feeds WHERE address = ?"
cur.execute(sql, (url,)) cur.execute(sql, (url,))
return cur.fetchone() return cur.fetchone()
async def add_feed(conn, lock, url):
async def add_feed(db_file, url):
""" """
Add a new feed into the feeds table Add a new feed into the feeds table
:param conn: :param conn:
:param feed: :param feed:
:return: string :return: string
""" """
async with lock: #TODO consider async with DBLOCK
print("add_feed(conn, lock, url)")
#TODO consider async with lock
#conn = create_connection(db_file) #conn = create_connection(db_file)
cur = conn.cursor() with create_connection(db_file) as conn:
print(time.strftime("%H:%M:%S"), "conn.cursor() from add_feed(conn, url)") exist = await check_feed(conn, url)
exist = await check_feed(conn, lock, url)
if not exist: if not exist:
res = await download_feed(conn, url) res = await download_feed(url)
else:
return "News source is already listed in the subscription list"
async with DBLOCK:
with create_connection(db_file) as conn:
cur = conn.cursor()
if res[0]: if res[0]:
feed = feedparser.parse(res[0]) feed = feedparser.parse(res[0])
if feed.bozo: if feed.bozo:
@ -513,7 +412,6 @@ async def add_feed(conn, lock, url):
sql = """INSERT INTO feeds(address,enabled,status,valid) sql = """INSERT INTO feeds(address,enabled,status,valid)
VALUES(?,?,?,?) """ VALUES(?,?,?,?) """
cur.execute(sql, feed) cur.execute(sql, feed)
conn.commit()
bozo = ("WARNING: Bozo detected. Failed to load URL.") bozo = ("WARNING: Bozo detected. Failed to load URL.")
print(bozo) print(bozo)
return "Failed to parse URL as feed" return "Failed to parse URL as feed"
@ -523,137 +421,98 @@ async def add_feed(conn, lock, url):
sql = """INSERT INTO feeds(name,address,enabled,status,valid) sql = """INSERT INTO feeds(name,address,enabled,status,valid)
VALUES(?,?,?,?,?) """ VALUES(?,?,?,?,?) """
cur.execute(sql, feed) cur.execute(sql, feed)
conn.commit()
else: else:
feed = (url, 1, res[1], 0) feed = (url, 1, res[1], 0)
sql = """INSERT INTO feeds(address,enabled,status,valid) sql = "INSERT INTO feeds(address,enabled,status,valid) VALUES(?,?,?,?) "
VALUES(?,?,?,?) """
cur.execute(sql, feed) cur.execute(sql, feed)
conn.commit()
return "Failed to get URL. HTTP Error {}".format(res[1]) return "Failed to get URL. HTTP Error {}".format(res[1])
print(time.strftime("%H:%M:%S"), "conn.commit() from add_feed(conn, url)")
# source = title if not '' else url
source = title if title else '<' + url + '>' source = title if title else '<' + url + '>'
msg = """News source "{}" has been added to subscriptions list msg = 'News source "{}" has been added to subscriptions list'.format(source)
""".format(source)
else:
msg = "News source is already listed in the subscription list"
return msg return msg
async def remove_feed(conn, lock, id):
async def remove_feed(db_file, ix):
""" """
Delete a feed by feed id Delete a feed by feed id
:param conn: :param conn:
:param id: id of the feed :param id: id of the feed
:return: string :return: string
""" """
async with lock: with create_connection(db_file) as conn:
print("remove_feed(conn, lock, id)")
# You have chose to remove feed (title, url) from your feed list.
# Enter "delete" to confirm removal.
#conn = create_connection(db_file)
cur = conn.cursor() cur = conn.cursor()
print(time.strftime("%H:%M:%S"), "conn.cursor() from remove_feed(conn, id)")
sql = "SELECT address FROM feeds WHERE id = ?" sql = "SELECT address FROM feeds WHERE id = ?"
# NOTE [0][1][2] url = cur.execute(sql, (ix,))
url = cur.execute(sql, (id,))
for i in url: for i in url:
url = i[0] url = i[0]
sql = "DELETE FROM entries WHERE source = ?" sql = "DELETE FROM entries WHERE source = ?"
cur.execute(sql, (url,)) cur.execute(sql, (url,))
sql = "DELETE FROM feeds WHERE id = ?" sql = "DELETE FROM feeds WHERE id = ?"
cur.execute(sql, (id,)) cur.execute(sql, (ix,))
conn.commit()
print(time.strftime("%H:%M:%S"), "conn.commit() from remove_feed(conn, id)")
return """News source <{}> has been removed from subscriptions list return """News source <{}> has been removed from subscriptions list
""".format(url) """.format(url)
async def get_unread(conn, lock):
async def get_unread(db_file):
""" """
Check read status of entry Check read status of entry
:param conn: :param conn:
:param id: id of the entry :param id: id of the entry
:return: string :return: string
""" """
with conn: async with DBLOCK:
with create_connection(db_file) as conn:
entry = [] entry = []
cur = conn.cursor() cur = conn.cursor()
print(time.strftime("%H:%M:%S"), "conn.cursor() from get_unread(conn)")
sql = "SELECT id FROM entries WHERE read = 0" sql = "SELECT id FROM entries WHERE read = 0"
#id = cur.execute(sql).fetchone()[0] ix = cur.execute(sql).fetchone()
id = cur.execute(sql).fetchone() if ix is None:
if id is None:
return False return False
id = id[0] ix = ix[0]
sql = "SELECT title FROM entries WHERE id = :id" sql = "SELECT title FROM entries WHERE id = :id"
cur.execute(sql, (id,)) cur.execute(sql, (ix,))
title = cur.fetchone()[0] title = cur.fetchone()[0]
entry.append(title) entry.append(title)
sql = "SELECT summary FROM entries WHERE id = :id" sql = "SELECT summary FROM entries WHERE id = :id"
cur.execute(sql, (id,)) cur.execute(sql, (ix,))
summary = cur.fetchone()[0] summary = cur.fetchone()[0]
entry.append(summary) entry.append(summary)
sql = "SELECT link FROM entries WHERE id = :id" sql = "SELECT link FROM entries WHERE id = :id"
cur.execute(sql, (id,)) cur.execute(sql, (ix,))
link = cur.fetchone()[0] link = cur.fetchone()[0]
entry.append(link) entry.append(link)
# columns = ['title', 'summary', 'link']
# for column in columns:
# sql = "SELECT :column FROM entries WHERE id = :id"
# cur.execute(sql, {"column": column, "id": id})
# str = cur.fetchone()[0]
# entry.append(str)
entry = "{}\n\n{}\n\nLink to article:\n{}".format(entry[0], entry[1], entry[2]) entry = "{}\n\n{}\n\nLink to article:\n{}".format(entry[0], entry[1], entry[2])
await mark_as_read(conn, lock, id) await mark_as_read(cur, ix)
return entry return entry
async def mark_as_read(conn, lock, id):
async def mark_as_read(cur, ix):
""" """
Set read status of entry Set read status of entry
:param conn: :param cur:
:param id: id of the entry :param ix: index of the entry
""" """
async with lock:
cur = conn.cursor()
print(time.strftime("%H:%M:%S"), "conn.cursor() from mark_as_read(conn, id)")
sql = "UPDATE entries SET summary = '', read = 1 WHERE id = ?" sql = "UPDATE entries SET summary = '', read = 1 WHERE id = ?"
cur.execute(sql, (id,)) cur.execute(sql, (ix,))
conn.commit()
print(time.strftime("%H:%M:%S"), "conn.commit() from mark_as_read(conn, id)")
#conn.close()
async def feed_refresh(conn, lock, id):
async with lock:
cur = conn.cursor()
sql = "SELECT address FROM feeds WHERE id = :id"
cur.execute(sql, (id,))
url = cur.fetchone()[0]
res = await download_feed(conn, url)
feed = feedparser.parse(res[0])
title = feed["feed"]["title"]
sql = "UPDATE feeds SET name = :name WHERE address = :url"
cur.execute(sql, {"name": title, "url": url})
conn.commit()
# TODO mark_all_read for entries of feed # TODO mark_all_read for entries of feed
async def toggle_status(conn, lock, id): async def toggle_status(db_file, ix):
""" """
Set status of feed Set status of feed
:param conn: :param conn:
:param id: id of the feed :param id: id of the feed
:return: string :return: string
""" """
print("toggle_status(conn, lock, id)") async with DBLOCK:
async with lock: with create_connection(db_file) as conn:
#conn = create_connection(db_file)
cur = conn.cursor() cur = conn.cursor()
print(time.strftime("%H:%M:%S"), "conn.cursor() from toggle_status(conn, id)")
sql = "SELECT name FROM feeds WHERE id = :id" sql = "SELECT name FROM feeds WHERE id = :id"
cur.execute(sql, (id,)) cur.execute(sql, (ix,))
title = cur.fetchone()[0] title = cur.fetchone()[0]
sql = "SELECT enabled FROM feeds WHERE id = ?" sql = "SELECT enabled FROM feeds WHERE id = ?"
# NOTE [0][1][2] # NOTE [0][1][2]
cur.execute(sql, (id,)) cur.execute(sql, (ix,))
status = cur.fetchone()[0] status = cur.fetchone()[0]
# FIXME always set to 1 # FIXME always set to 1
# NOTE Maybe because is not integer # NOTE Maybe because is not integer
@ -665,12 +524,11 @@ async def toggle_status(conn, lock, id):
status = 1 status = 1
notice = "News updates for '{}' are now enabled".format(title) notice = "News updates for '{}' are now enabled".format(title)
sql = "UPDATE feeds SET enabled = :status WHERE id = :id" sql = "UPDATE feeds SET enabled = :status WHERE id = :id"
cur.execute(sql, {"status": status, "id": id}) cur.execute(sql, {"status": status, "id": ix})
conn.commit()
print(time.strftime("%H:%M:%S"), "conn.commit() from toggle_status(conn, id)")
return notice return notice
async def toggle_state(jid, state):
def toggle_state(jid, state):
""" """
Set status of update Set status of update
:param jid: jid of the user :param jid: jid of the user
@ -678,28 +536,22 @@ async def toggle_state(jid, state):
:return: :return:
""" """
db_dir = get_default_dbdir() db_dir = get_default_dbdir()
os.chdir(db_dir) db_file = os.path.join(db_dir, r"{}.db".format(jid))
db_file = r"{}.db".format(jid) bk_file = os.path.join(db_dir, r"{}.db.bak".format(jid))
bk_file = r"{}.db.bak".format(jid)
if state: if state:
if path.exists(db_file): if os.path.exists(db_file):
return "Updates are already enabled" return "Updates are already enabled"
elif path.exists(bk_file): elif os.path.exists(bk_file):
os.renames(bk_file, db_file) os.renames(bk_file, db_file)
return "Updates are now enabled" return "Updates are now enabled"
else: else:
if path.exists(bk_file): if os.path.exists(bk_file):
return "Updates are already disabled" return "Updates are already disabled"
elif path.exists(db_file): elif os.path.exists(db_file):
os.renames(db_file, bk_file) os.renames(db_file, bk_file)
return "Updates are now disabled" return "Updates are now disabled"
# if path.exists(db_file):
# os.renames(db_file, db_file + ".bak")
# break
# db_file = r"{}.db.bak".format(jid)
# if path.exists(db_file):
# os.renames(db_file, jid,+".db")
async def set_date(conn, url): async def set_date(conn, url):
""" """
@ -709,11 +561,9 @@ async def set_date(conn, url):
""" """
today = date.today() today = date.today()
cur = conn.cursor() cur = conn.cursor()
print(time.strftime("%H:%M:%S"), "conn.cursor() from set_date(conn, url)")
sql = "UPDATE feeds SET updated = :today WHERE address = :url" sql = "UPDATE feeds SET updated = :today WHERE address = :url"
cur.execute(sql, {"today": today, "url": url}) cur.execute(sql, {"today": today, "url": url})
conn.commit()
print(time.strftime("%H:%M:%S"), "conn.commit() from set_date(conn, url)")
async def get_subscriptions(conn): async def get_subscriptions(conn):
""" """
@ -722,28 +572,26 @@ async def get_subscriptions(conn):
:return: rows (tuple) :return: rows (tuple)
""" """
cur = conn.cursor() cur = conn.cursor()
print(time.strftime("%H:%M:%S"), "conn.cursor() from get_subscriptions(conn)")
sql = "SELECT address FROM feeds WHERE enabled = 1" sql = "SELECT address FROM feeds WHERE enabled = 1"
result = cur.execute(sql) result = cur.execute(sql)
return result return result
async def list_subscriptions(conn):
async def list_subscriptions(db_file):
""" """
Query feeds Query feeds
:param conn: :param conn:
:return: rows (string) :return: rows (string)
""" """
print("list_subscriptions(conn)") with create_connection(db_file) as conn:
cur = conn.cursor() cur = conn.cursor()
print(time.strftime("%H:%M:%S"), "conn.cursor() from list_subscriptions(conn)")
#sql = "SELECT id, address FROM feeds"
sql = "SELECT name, address, updated, id, enabled FROM feeds" sql = "SELECT name, address, updated, id, enabled FROM feeds"
results = cur.execute(sql) results = cur.execute(sql)
feeds_list = "List of subscriptions: \n" feeds_list = "List of subscriptions: \n"
counter = 0 counter = 0
for result in results: for result in results:
counter += 1 counter += 1
#feeds_list = feeds_list + '\n {}. {}'.format(str(result[0]), str(result[1]))
feeds_list += """\n{} \n{} \nLast updated: {} \nID: {} [{}] feeds_list += """\n{} \n{} \nLast updated: {} \nID: {} [{}]
""".format(str(result[0]), str(result[1]), str(result[2]), """.format(str(result[0]), str(result[1]), str(result[2]),
str(result[3]), str(result[4])) str(result[3]), str(result[4]))
@ -757,48 +605,49 @@ async def list_subscriptions(conn):
"feed add https://reclaimthenet.org/feed/") "feed add https://reclaimthenet.org/feed/")
return msg return msg
async def last_entries(conn, num):
async def last_entries(db_file, num):
""" """
Query feeds Query feeds
:param conn: :param conn:
:param num: integer :param num: integer
:return: rows (string) :return: rows (string)
""" """
print("last_entries(conn, num)")
num = int(num) num = int(num)
if num > 50: if num > 50:
num = 50 num = 50
elif num < 1: elif num < 1:
num = 1 num = 1
with create_connection(db_file) as conn:
cur = conn.cursor() cur = conn.cursor()
sql = "SELECT title, link FROM entries ORDER BY ROWID DESC LIMIT {}".format(num) sql = "SELECT title, link FROM entries ORDER BY ROWID DESC LIMIT {}".format(num)
results = cur.execute(sql) results = cur.execute(sql)
titles_list = "Recent {} titles: \n".format(num) titles_list = "Recent {} titles: \n".format(num)
for result in results: for result in results:
# titles_list += """\nTitle: {} \nLink: {} titles_list += "\n{} \n{}".format(str(result[0]), str(result[1]))
titles_list += """\n{} \n{}
""".format(str(result[0]), str(result[1]))
return titles_list return titles_list
async def search_entries(conn, query):
async def search_entries(db_file, query):
""" """
Query feeds Query feeds
:param conn: :param conn:
:param query: string :param query: string
:return: rows (string) :return: rows (string)
""" """
print("search_entries(conn, query)")
if len(query) < 2: if len(query) < 2:
return "Please enter at least 2 characters to search" return "Please enter at least 2 characters to search"
with create_connection(db_file) as conn:
cur = conn.cursor() cur = conn.cursor()
sql = "SELECT title, link FROM entries WHERE title LIKE '%{}%' LIMIT 50".format(query) sql = "SELECT title, link FROM entries WHERE title LIKE '%{}%' LIMIT 50".format(query)
# sql = "SELECT title, link FROM entries WHERE title OR link LIKE '%{}%'".format(query)
results = cur.execute(sql) results = cur.execute(sql)
results_list = "Search results for '{}': \n".format(query) results_list = "Search results for '{}': \n".format(query)
counter = 0 counter = 0
for result in results: for result in results:
counter += 1 counter += 1
# titles_list += """\nTitle: {} \nLink: {}
results_list += """\n{} \n{} results_list += """\n{} \n{}
""".format(str(result[0]), str(result[1])) """.format(str(result[0]), str(result[1]))
if counter: if counter:
@ -806,6 +655,7 @@ async def search_entries(conn, query):
else: else:
return "No results found for: {}".format(query) return "No results found for: {}".format(query)
async def check_entry(conn, title, link): async def check_entry(conn, title, link):
""" """
Check whether an entry exists Check whether an entry exists
@ -816,11 +666,11 @@ async def check_entry(conn, title, link):
:return: row :return: row
""" """
cur = conn.cursor() cur = conn.cursor()
print(time.strftime("%H:%M:%S"), "conn.cursor() from check_entry(conn, title, link)")
sql = "SELECT id FROM entries WHERE title = :title and link = :link" sql = "SELECT id FROM entries WHERE title = :title and link = :link"
cur.execute(sql, {"title": title, "link": link}) cur.execute(sql, {"title": title, "link": link})
return cur.fetchone() return cur.fetchone()
async def add_entry(conn, entry): async def add_entry(conn, entry):
""" """
Add a new entry into the entries table Add a new entry into the entries table
@ -831,10 +681,8 @@ async def add_entry(conn, entry):
sql = """ INSERT INTO entries(title,summary,link,source,read) sql = """ INSERT INTO entries(title,summary,link,source,read)
VALUES(?,?,?,?,?) """ VALUES(?,?,?,?,?) """
cur = conn.cursor() cur = conn.cursor()
print(time.strftime("%H:%M:%S"), "conn.cursor() from add_entry(conn, entry)")
cur.execute(sql, entry) cur.execute(sql, entry)
conn.commit()
print(time.strftime("%H:%M:%S"), "conn.commit() from add_entry(conn, entry)")
async def remove_entry(conn, source, length): async def remove_entry(conn, source, length):
""" """
@ -847,7 +695,6 @@ async def remove_entry(conn, source, length):
:return: :return:
""" """
cur = conn.cursor() cur = conn.cursor()
print(time.strftime("%H:%M:%S"), "conn.cursor() from remove_entry(conn, source, length)")
# FIXED # FIXED
# Dino empty titles are not counted https://dino.im/index.xml # Dino empty titles are not counted https://dino.im/index.xml
# SOLVED # SOLVED
@ -856,10 +703,8 @@ async def remove_entry(conn, source, length):
sql = "SELECT count(id) FROM entries WHERE source = ?" sql = "SELECT count(id) FROM entries WHERE source = ?"
count = cur.execute(sql, (source,)) count = cur.execute(sql, (source,))
count = cur.fetchone()[0] count = cur.fetchone()[0]
#limit = count - length
limit = count - length limit = count - length
if limit: if limit:
#if limit > 0:
limit = limit; limit = limit;
sql = """DELETE FROM entries WHERE id IN ( sql = """DELETE FROM entries WHERE id IN (
SELECT id FROM entries SELECT id FROM entries
@ -867,20 +712,23 @@ async def remove_entry(conn, source, length):
ORDER BY id ORDER BY id
ASC LIMIT :limit)""" ASC LIMIT :limit)"""
cur.execute(sql, {"source": source, "limit": limit}) cur.execute(sql, {"source": source, "limit": limit})
conn.commit()
print(time.strftime("%H:%M:%S"), "conn.commit() from remove_entry(conn, source, length)")
if __name__ == '__main__': if __name__ == '__main__':
# Setup the command line arguments. # Setup the command line arguments.
parser = ArgumentParser(description=Slixfeed.__doc__) parser = ArgumentParser(description=Slixfeed.__doc__)
# Output verbosity options. # Output verbosity options.
parser.add_argument("-q", "--quiet", help="set logging to ERROR", parser.add_argument(
"-q", "--quiet", help="set logging to ERROR",
action="store_const", dest="loglevel", action="store_const", dest="loglevel",
const=logging.ERROR, default=logging.INFO) const=logging.ERROR, default=logging.INFO
parser.add_argument("-d", "--debug", help="set logging to DEBUG", )
parser.add_argument(
"-d", "--debug", help="set logging to DEBUG",
action="store_const", dest="loglevel", action="store_const", dest="loglevel",
const=logging.DEBUG, default=logging.INFO) const=logging.DEBUG, default=logging.INFO
)
# JID and password options. # JID and password options.
parser.add_argument("-j", "--jid", dest="jid", parser.add_argument("-j", "--jid", dest="jid",