bbbbbs/main.py

355 lines
15 KiB
Python
Executable File

#!/usr/bin/python3
import gi
import config
import logging
import schedule
import sqlite3
from threading import Thread
from datetime import datetime
from time import sleep
from random import choice
from string import ascii_lowercase, digits
from playsound import playsound
import pyttsx3
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
import db
ttsEngine = pyttsx3.init('espeak')
ttsEngine.setProperty('voice', 'female3')
ttsEngine.setProperty('speed', 40)
log = logging.getLogger("main")
def play_transmissions(timeslot):
schedulerEngine = pyttsx3.init('espeak')
schedulerEngine.setProperty('voice', 'female3')
schedulerEngine.setProperty('speed', 40)
log = logging.getLogger("main")
try:
schedulerConnection = sqlite3.connect(db.database)
schedulerCursor = schedulerConnection.cursor()
schedulerCursor.execute('select sqlite_version();')
except sqlite3.Error as error:
log.critical('Could not connect scheduler to sqlite. An error occured: ', error)
exit(1)
date = datetime.now().date().isoformat()
toPlay = db.get_transmissions_by_timeslot(date, timeslot, schedulerCursor)
for transmission in toPlay:
message = transmission["message"]
author = transmission["author"]
title = transmission["title"]
text = "Author: %s, Title: %s. Message: %s" % (
author,
title,
message)
schedulerEngine.say(text)
schedulerEngine.runAndWait()
schedulerConnection.close()
for i in range(len(config.timeslots)):
schedule.every().day.at(config.timeslots[i]).do(play_transmissions, i)
def scheduler_loop():
while True:
schedule.run_pending()
sleep(30)
scheduler_thread = Thread(target=scheduler_loop)
scheduler_thread.daemon = True
scheduler_thread.start()
db.create_transmissions_table()
class MainWindow(Gtk.Window):
def __init__(self):
super().__init__(title="bardzo biedny BBS %s"%config.version)
############################## Boxes #####################################
pad=15
self.main_box = Gtk.Box()
self.main_box.set_orientation(Gtk.Orientation.HORIZONTAL)
# left side
self.left_box = Gtk.Box()
self.left_box.set_orientation(Gtk.Orientation.VERTICAL)
self.left_box.set_spacing(0)
self.left_button_box = Gtk.Box()
self.left_button_box.set_orientation(Gtk.Orientation.HORIZONTAL)
self.left_button_box.set_spacing(pad)
# right side
self.right_box = Gtk.Box()
self.right_box.set_orientation(Gtk.Orientation.VERTICAL)
self.right_box.set_spacing(0)
self.title_author_box = Gtk.Box()
self.title_author_box.set_orientation(Gtk.Orientation.HORIZONTAL)
self.title_author_box.set_spacing(pad)
self.message_box = Gtk.Box()
self.message_box.set_orientation(Gtk.Orientation.VERTICAL)
self.message_box.set_spacing(2)
self.date_time_box = Gtk.Box()
self.date_time_box.set_orientation(Gtk.Orientation.HORIZONTAL)
self.date_time_box.set_spacing(pad)
self.timeslot_enable_box = Gtk.Box()
self.timeslot_enable_box.set_orientation(Gtk.Orientation.VERTICAL)
self.timeslot_enable_box.set_spacing(pad)
self.play_box = Gtk.Box()
self.play_box.set_orientation(Gtk.Orientation.HORIZONTAL)
self.play_box.set_spacing(pad)
self.clear_play_box = Gtk.Box()
self.clear_play_box.set_orientation(Gtk.Orientation.HORIZONTAL)
self.clear_play_box.set_spacing(pad)
self.clear_save_box = Gtk.Box()
self.clear_save_box.set_orientation(Gtk.Orientation.HORIZONTAL)
self.clear_save_box.set_spacing(pad)
################################ Widgets ######################################
# left side
self.transmission_list = Gtk.ListBox()
self.transmission_list.connect("row-selected", self.on_picked_transmission)
self.transmissions = []
self.update_transmission_list()
self.add_transmission_button = Gtk.Button(label="Add transmission")
self.add_transmission_button.connect("clicked", self.on_add_transmission)
self.delete_transmission_button = Gtk.Button(label="Delete transmission")
self.delete_transmission_button.connect("clicked", self.on_delete_transmission)
# right side
self.title = Gtk.Entry()
self.title.set_placeholder_text("Title")
self.title.connect("changed", self.update_save_button)
self.author = Gtk.Entry()
self.author.set_placeholder_text("Author")
self.author.connect("changed", self.update_save_button)
self.message_label = Gtk.Label(label="Message")
self.message_scroll_window = Gtk.ScrolledWindow()
self.message = Gtk.TextView()
self.message.get_buffer().connect("changed", self.update_save_button)
self.message.set_wrap_mode(Gtk.WrapMode.WORD)
self.calendar = Gtk.Calendar()
self.calendar.connect("day-selected", self.on_date_picked)
year, month, day = self.calendar.get_date()
month += 1
month = str(month).rjust(2, '0')
day = str(day).rjust(2, '0')
self.picked_date = "%i-%s-%s"%(year, month, day)
self.today = self.picked_date
self.timeslots = Gtk.ListBox()
self.timeslot_checkbuttons = []
for i in range(len(config.timeslots)):
slot = config.timeslots[i]
self.timeslot_checkbuttons.append(Gtk.CheckButton())
self.timeslot_checkbuttons[i].connect("toggled", self.update_save_button)
row = Gtk.ListBoxRow()
hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=50)
row.add(hbox)
time_label = Gtk.Label(label=slot, xalign=0)
hbox.pack_start(time_label, True, True, 0)
hbox.pack_start(self.timeslot_checkbuttons[i], False, True, 0)
self.timeslots.add(row)
self.play_label = Gtk.Label(label="enable this message?")
self.play_switch = Gtk.Switch()
self.play_button = Gtk.Button(label="play")
self.play_button.connect("clicked", self.on_play_clicked)
self.clear_button = Gtk.Button(label="clear")
self.clear_button.connect("clicked", self.clear_transmission)
self.save_button = Gtk.Button(label="save")
self.save_button.set_sensitive(False)
self.save_button.connect("clicked", self.on_transmission_saved)
# we need to put the buttons in a sizegroup to make the synthesize button the same length as the rest
self.right_sizegroup = Gtk.SizeGroup(mode=Gtk.SizeGroupMode.HORIZONTAL)
self.right_sizegroup.add_widget(self.play_button)
self.right_sizegroup.add_widget(self.clear_button)
self.right_sizegroup.add_widget(self.save_button)
######################### organise objects ###################################
self.add(self.main_box)
self.main_box.pack_start(self.left_box, True, True, pad)
self.main_box.pack_start(self.right_box, True, True, pad)
self.left_box.pack_start(self.transmission_list, True, True, pad)
self.left_box.pack_start(self.left_button_box, False, True, pad)
self.left_button_box.pack_start(self.add_transmission_button, True, True, 0)
self.left_button_box.pack_start(self.delete_transmission_button, True, True, 0)
self.right_box.pack_start(self.title_author_box, False, True, pad)
self.title_author_box.pack_start(self.title, True, True, 0)
self.title_author_box.pack_start(self.author, True, True, 0)
self.right_box.pack_start(self.message_box, True, True, pad)
self.message_box.pack_start(self.message_label, False, True, 0)
self.message_box.pack_start(self.message_scroll_window, True, True, 0)
self.message_scroll_window.add(self.message)
self.right_box.pack_start(self.date_time_box, True, True, pad)
self.date_time_box.pack_start(self.calendar, True, True, 0)
self.date_time_box.pack_start(self.timeslot_enable_box, True, True, 0)
self.timeslot_enable_box.pack_start(self.timeslots, True, True, 0)
self.timeslot_enable_box.pack_start(self.play_box, False, True, 0)
self.play_box.pack_start(self.play_label, True, True, 0)
self.play_box.pack_start(self.play_switch, False, False, 0)
self.right_box.pack_start(self.clear_play_box, False, True, 0)
self.clear_play_box.pack_start(self.clear_button, True, True, 0)
self.clear_play_box.pack_start(self.play_button, True, True, 0)
self.right_box.pack_start(self.clear_save_box, False, True, pad)
self.clear_save_box.pack_start(self.save_button, True, True, 0)
############################## Helper functions #############################
# The save button should be greyed out until all the data is input
def update_save_button(self, user_data):
messageBuffer = self.message.get_buffer()
startIter, endIter = messageBuffer.get_bounds()
message = messageBuffer.get_text(startIter, endIter, False)
if self.author.get_text() != "" and self.title.get_text() != "" and message != "" and self.get_selected_timeslots() != "":
self.save_button.set_sensitive(True)
else:
self.save_button.set_sensitive(False)
def on_date_picked(self, user_data):
year, month, day = self.calendar.get_date()
month += 1
month = str(month).rjust(2, '0')
day = str(day).rjust(2, '0')
self.picked_date = "%i-%s-%s"%(year, month, day)
def get_selected_timeslots(self):
selected = []
for i in range(len(config.timeslots)):
if self.timeslot_checkbuttons[i].get_active():
selected.append(str(i))
return str(selected)
def update_transmission_list(self):
self.transmissions = db.get_transmissions()
for child in self.transmission_list.get_children():
self.transmission_list.remove(child)
for i in range(len(self.transmissions)):
trx = self.transmissions[i]
trxID = "%s by %s at %s" % (trx[3], trx[2], trx[1]) # TODO: replace with dictionary for readability
label = Gtk.Label(label=trxID)
if not trx[-1]: # if the transmission is not to be played
label.set_markup('<span foreground="red">%s</span>'%trxID)
self.transmission_list.insert(label, -1)
self.transmission_list.show_all()
def clear_transmission(self, user_data=None):
self.author.set_text("")
self.title.set_text("")
self.message.get_buffer().set_text("")
year, month, day = self.today.split("-")
self.calendar.select_day(int(day))
self.calendar.select_month(int(month)-1, int(year))
for i in range(len(self.timeslots)):
self.timeslot_checkbuttons[int(i)].set_active(False)
self.play_switch.set_state(False)
############################ Button functions ###############################
def on_add_transmission(self, user_data):
uid = "".join(choice(ascii_lowercase + digits) for char in range(8))
log.info("creating transmission %s" % uid)
log.debug(uid)
db.add_transmission(uid)
self.update_transmission_list()
self.transmission_list.select_row(self.transmission_list.get_row_at_index(len(self.transmissions)-1))
def on_delete_transmission(self, user_data):
uid = self.transmissions[self.transmission_list.get_selected_row().get_index()][0]
log.info("deleting transmission %s" % uid)
db.delete_transmission(uid)
self.update_transmission_list()
self.transmission_list.select_row(self.transmission_list.get_row_at_index(len(self.transmissions)-1))
def on_picked_transmission(self, row, user_data):
try:
log.debug("row %i activated" % self.transmission_list.get_selected_row().get_index())
uid = self.transmissions[self.transmission_list.get_selected_row().get_index()][0]
trxData = db.get_transmission_data(uid)
except AttributeError:
# when adding a new transmission, this function is called before any row is selected and logs
# an error. I don't know when or why that happens, but this does the job.
return
except:
log.critical(error)
exit(1)
try:
self.clear_transmission()
self.author.set_text(trxData["author"])
self.title.set_text(trxData["title"])
self.message.get_buffer().set_text(trxData["message"])
year, month, day = trxData["date"].split("-")
self.calendar.select_day(int(day))
self.calendar.select_month(int(month)-1, int(year))
timeSlots = eval(trxData["timeslots"])
for i in timeSlots:
self.timeslot_checkbuttons[int(i)].set_active(True)
self.play_switch.set_state(trxData["play"])
except TypeError:
self.clear_transmission()
def on_play_clicked(self, user_data):
messageBuffer = self.message.get_buffer()
startIter, endIter = messageBuffer.get_bounds()
message = messageBuffer.get_text(startIter, endIter, False)
text = "Author: %s, Title: %s. Message: %s" % (
self.author.get_buffer().get_text(),\
self.title.get_buffer().get_text(),\
message)
ttsEngine.say(text)
ttsEngine.runAndWait()
def on_transmission_saved(self, user_data):
uid = self.transmissions[self.transmission_list.get_selected_row().get_index()][0]
log.info("saving transmission %s" % uid)
messageBuffer = self.message.get_buffer()
startIter, endIter = messageBuffer.get_bounds()
message = messageBuffer.get_text(startIter, endIter, False)
db.edit_transmission(uid,
self.picked_date,\
self.author.get_buffer().get_text(),\
self.title.get_buffer().get_text(),\
message,
self.get_selected_timeslots(),\
1,\
self.play_switch.get_state())
self.update_transmission_list()
for i in range(len(self.transmissions)):
if self.transmissions[i][0] == uid:
self.transmission_list.select_row(self.transmission_list.get_row_at_index(i))
win = MainWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()
db.sqliteConnection.close()
log.debug("database connection closed")