mirror of
https://github.com/Lynnesbian/FediBooks/
synced 2024-11-25 16:48:58 +00:00
Compare commits
22 commits
c060f8d7ca
...
8f476ca981
Author | SHA1 | Date | |
---|---|---|---|
8f476ca981 | |||
ec758d9d8f | |||
bca0da6c45 | |||
9bbc669fcc | |||
f4465219d0 | |||
ca6e06668f | |||
bf10de0e17 | |||
dc03ebed18 | |||
fad45bd6d3 | |||
9306a72b99 | |||
46118ae9fa | |||
494c1d8960 | |||
bf7571eba1 | |||
913b5ed338 | |||
3b7b479b1b | |||
887aab6a71 | |||
8f72d14243 | |||
950db1de8e | |||
879b53889d | |||
4230986c2b | |||
a5d775d5e7 | |||
1d3f9fbc73 |
11 changed files with 217 additions and 69 deletions
16
setup.sql
16
setup.sql
|
@ -15,10 +15,9 @@ CREATE TABLE IF NOT EXISTS `credentials` (
|
||||||
`secret` VARCHAR(128) NOT NULL
|
`secret` VARCHAR(128) NOT NULL
|
||||||
) ENGINE=INNODB;
|
) ENGINE=INNODB;
|
||||||
CREATE TABLE IF NOT EXISTS `bots` (
|
CREATE TABLE IF NOT EXISTS `bots` (
|
||||||
`id` BINARY(64) PRIMARY KEY,
|
`handle` VARCHAR(128) PRIMARY KEY,
|
||||||
`user_id` INT NOT NULL,
|
`user_id` INT NOT NULL,
|
||||||
`credentials_id` INT NOT NULL,
|
`credentials_id` INT NOT NULL,
|
||||||
`handle` VARCHAR(128) NOT NULL,
|
|
||||||
`enabled` BOOLEAN DEFAULT 1,
|
`enabled` BOOLEAN DEFAULT 1,
|
||||||
`replies_enabled` BOOLEAN DEFAULT 1,
|
`replies_enabled` BOOLEAN DEFAULT 1,
|
||||||
`post_frequency` SMALLINT UNSIGNED DEFAULT 30,
|
`post_frequency` SMALLINT UNSIGNED DEFAULT 30,
|
||||||
|
@ -37,15 +36,16 @@ CREATE TABLE IF NOT EXISTS `bots` (
|
||||||
CREATE TABLE IF NOT EXISTS `fedi_accounts` (
|
CREATE TABLE IF NOT EXISTS `fedi_accounts` (
|
||||||
`handle` VARCHAR(128) PRIMARY KEY,
|
`handle` VARCHAR(128) PRIMARY KEY,
|
||||||
`outbox` VARCHAR(256),
|
`outbox` VARCHAR(256),
|
||||||
`credentials_id` INT NOT NULL,
|
`credentials_id` INT,
|
||||||
`icon` VARCHAR(512),
|
`icon` VARCHAR(512),
|
||||||
`icon_update_time` DATETIME DEFAULT 0,
|
`icon_update_time` DATETIME DEFAULT 0,
|
||||||
FOREIGN KEY (`credentials_id`) REFERENCES credentials(id) ON DELETE CASCADE
|
FOREIGN KEY (`credentials_id`) REFERENCES credentials(id) ON DELETE CASCADE
|
||||||
) ENGINE=INNODB;
|
) ENGINE=INNODB;
|
||||||
CREATE TABLE IF NOT EXISTS `bot_learned_accounts` (
|
CREATE TABLE IF NOT EXISTS `bot_learned_accounts` (
|
||||||
`bot_id` BINARY(64) NOT NULL,
|
`bot_id` VARCHAR(128) NOT NULL,
|
||||||
`fedi_id` VARCHAR(128) NOT NULL,
|
`fedi_id` VARCHAR(128) NOT NULL,
|
||||||
FOREIGN KEY (`bot_id`) REFERENCES bots(id) ON DELETE CASCADE,
|
`enabled` BOOLEAN DEFAULT 1,
|
||||||
|
FOREIGN KEY (`bot_id`) REFERENCES bots(handle) ON DELETE CASCADE,
|
||||||
FOREIGN KEY (`fedi_id`) REFERENCES fedi_accounts(handle) ON DELETE CASCADE
|
FOREIGN KEY (`fedi_id`) REFERENCES fedi_accounts(handle) ON DELETE CASCADE
|
||||||
) ENGINE=INNODB;
|
) ENGINE=INNODB;
|
||||||
CREATE TABLE IF NOT EXISTS `posts` (
|
CREATE TABLE IF NOT EXISTS `posts` (
|
||||||
|
@ -54,14 +54,14 @@ CREATE TABLE IF NOT EXISTS `posts` (
|
||||||
`post_id` VARCHAR(64) NOT NULL,
|
`post_id` VARCHAR(64) NOT NULL,
|
||||||
`content` TEXT NOT NULL,
|
`content` TEXT NOT NULL,
|
||||||
`cw` BOOLEAN NOT NULL,
|
`cw` BOOLEAN NOT NULL,
|
||||||
FOREIGN KEY (`fedi_id`) REFERENCES fedi_account(handle) ON DELETE CASCADE
|
FOREIGN KEY (`fedi_id`) REFERENCES fedi_accounts(handle) ON DELETE CASCADE
|
||||||
) ENGINE=INNODB;
|
) ENGINE=INNODB;
|
||||||
CREATE TABLE IF NOT EXISTS `word_blacklist` (
|
CREATE TABLE IF NOT EXISTS `word_blacklist` (
|
||||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
`bot_id` BINARY(64) NOT NULL,
|
`bot_id` VARCHAR(128) NOT NULL,
|
||||||
`phrase` VARCHAR(128) NOT NULL,
|
`phrase` VARCHAR(128) NOT NULL,
|
||||||
`whole_word` BOOLEAN NOT NULL,
|
`whole_word` BOOLEAN NOT NULL,
|
||||||
FOREIGN KEY (`bot_id`) REFERENCES bots(id) ON DELETE CASCADE
|
FOREIGN KEY (`bot_id`) REFERENCES bots(handle) ON DELETE CASCADE
|
||||||
) ENGINE=INNODB;
|
) ENGINE=INNODB;
|
||||||
CREATE TABLE IF NOT EXISTS `contact_history` (
|
CREATE TABLE IF NOT EXISTS `contact_history` (
|
||||||
`user_id` INT NOT NULL,
|
`user_id` INT NOT NULL,
|
||||||
|
|
BIN
static/bot_generic.png
Normal file
BIN
static/bot_generic.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.8 KiB |
|
@ -10,7 +10,7 @@ body {
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
background-color: #444a5c;
|
background-color: #444a5c;
|
||||||
padding:10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
.light {
|
.light {
|
||||||
background-color: #4d5366;
|
background-color: #4d5366;
|
||||||
|
@ -26,6 +26,9 @@ body {
|
||||||
.small {
|
.small {
|
||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
}
|
}
|
||||||
|
.tiny {
|
||||||
|
font-size: 0.5em;
|
||||||
|
}
|
||||||
.centred {
|
.centred {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
@ -45,7 +48,11 @@ body {
|
||||||
.panel-icon {
|
.panel-icon {
|
||||||
width: 100px;
|
width: 100px;
|
||||||
height: 100px;
|
height: 100px;
|
||||||
background: center/contain url("https://cloud.lynnesbian.space/s/JZCPTs3DzMLAspC/preview");
|
background: center/contain url("/img/bot_generic.png");
|
||||||
|
}
|
||||||
|
.panel-icon.large {
|
||||||
|
width: 150px;
|
||||||
|
height: 150px;
|
||||||
}
|
}
|
||||||
.panel-icon.online, .panel-icon.offline {
|
.panel-icon.online, .panel-icon.offline {
|
||||||
width: 105px;
|
width: 105px;
|
||||||
|
@ -74,20 +81,20 @@ body {
|
||||||
|
|
||||||
.button {
|
.button {
|
||||||
color: white;
|
color: white;
|
||||||
line-height:1.2em;
|
line-height: 1.2em;
|
||||||
padding:10px;
|
padding: 10px;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin: 5px 0;
|
margin: 5px 0;
|
||||||
transition: 0.2s linear;
|
transition: 0.2s linear;
|
||||||
border:none;
|
border: none;
|
||||||
}
|
}
|
||||||
.button:visited {
|
.button:visited {
|
||||||
color:white;
|
color: white;
|
||||||
}
|
}
|
||||||
input.button, button.button {
|
input.button, button.button {
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-primary {
|
.btn-primary {
|
||||||
|
@ -122,7 +129,7 @@ a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
a:visited {
|
a:visited {
|
||||||
color: white;
|
color: mediumpurple;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
|
@ -135,17 +142,17 @@ h1, h2, h3, h4, h5, h6 {
|
||||||
}
|
}
|
||||||
|
|
||||||
form {
|
form {
|
||||||
display:inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
label.important {
|
label.important {
|
||||||
font-size: 1.4em;
|
font-size: 1.4em;
|
||||||
margin: 10px 0;
|
margin: 10px 0;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
display:block;
|
display: block;
|
||||||
}
|
}
|
||||||
input, select {
|
input, select {
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
line-height:1.4em;
|
line-height: 1.4em;
|
||||||
border: 3px grey solid;
|
border: 3px grey solid;
|
||||||
border-radius: none;
|
border-radius: none;
|
||||||
padding: 3px;
|
padding: 3px;
|
||||||
|
@ -164,3 +171,10 @@ label {
|
||||||
form .row {
|
form .row {
|
||||||
margin: 10px 0;
|
margin: 10px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.coming-soon {
|
||||||
|
height: 200px;
|
||||||
|
width: 200px;
|
||||||
|
background: center/contain url("https://lynnesbian.space/img/bune.png");
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
<body>
|
<body>
|
||||||
<div class="container centred">
|
<div class="container centred">
|
||||||
<h1 class="thin centred">Set accounts to learn from</h1>
|
<h1 class="thin centred">Set accounts to learn from</h1>
|
||||||
<p class="large centred">@botname@example.com</p>
|
<p class="large centred">{{ handle }}</p>
|
||||||
<p class="centred" style="margin: 50px 0;">
|
<p class="centred" style="margin: 50px 0;">
|
||||||
<a class="button btn-primary btn-large" href="/bot/accounts/add" role="button"><i class="fas fa-plus"></i> Add account</a>
|
<a class="button btn-primary btn-large" href="/bot/accounts/add" role="button"><i class="fas fa-plus"></i> Add account</a>
|
||||||
<a class="button btn-secondary btn-large" href="/" role="button"><i class="fas fa-arrow-left"></i> Back</a>
|
<a class="button btn-secondary btn-large" href="/" role="button"><i class="fas fa-arrow-left"></i> Back</a>
|
||||||
|
@ -17,16 +17,19 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="container" style="min-height: 300px;">
|
<div class="container" style="min-height: 300px;">
|
||||||
|
{% for user in users %}
|
||||||
<div class="row light">
|
<div class="row light">
|
||||||
<div class="panel-icon"></div>
|
<div class="panel-icon {{ "online" if user['enabled'] else "offline" }}"></div>
|
||||||
<div class="panel-text">
|
<div class="panel-text">
|
||||||
<div class="panel-name">@user<span class="subtle">@instan.ce</span></div>
|
{% set handle_list = user['fedi_id'].split('@') %}
|
||||||
<div class="panel-status">12345 posts stored</div>
|
<div class="panel-name">@{{ handle_list[1] }}<span class="subtle tiny">@{{ handle_list[2] }}</span></div>
|
||||||
|
<div class="panel-status">{{ "Active" if user['enabled'] else "Inactive" }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-actions">
|
<div class="panel-actions">
|
||||||
<a class="button btn-secondary" href="/bot/accounts/toggle/insert id here" title="Turn on/off"><i class="fas fa-power-off"></i></a><a class="button btn-dangerous" href="/bot/accounts/delete/insert id here" title="Delete"><i class="fas fa-trash"></i></a>
|
<a class="button btn-secondary" href="/bot/accounts/toggle/{{ user['fedi_id'] }}" title="Turn on/off"><i class="fas fa-power-off"></i></a><a class="button btn-dangerous" href="/bot/accounts/delete/{{ user['fedi_id'] }}" title="Delete"><i class="fas fa-trash"></i></a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% include 'footer.html' %}
|
{% include 'footer.html' %}
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1 class="thin centred">Add account</h1>
|
<h1 class="thin centred">Add account</h1>
|
||||||
<p class="large thin centred">Step {{ session['step'] }}</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="container centred">
|
<div class="container centred">
|
||||||
|
@ -38,7 +37,7 @@
|
||||||
<div class="container centred">
|
<div class="container centred">
|
||||||
<a href="/" class="button btn-secondary"><i class="fas fa-times"></i> Cancel</a>
|
<a href="/" class="button btn-secondary"><i class="fas fa-times"></i> Cancel</a>
|
||||||
{% if session['step'] != 1 %}
|
{% if session['step'] != 1 %}
|
||||||
<button class="button btn-secondary"><i class="fas fa-arrow-left"></i> Back</button>
|
<a href="/bot/accounts/add/back" class="button btn-secondary"><i class="fas fa-arrow-left"></i> Back</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<button class="button btn-primary"><i class="fas fa-arrow-right"></i> Next</button>
|
<button class="button btn-primary"><i class="fas fa-arrow-right"></i> Next</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -40,9 +40,9 @@
|
||||||
<div class="container centred">
|
<div class="container centred">
|
||||||
<a href="/" class="button btn-secondary"><i class="fas fa-times"></i> Cancel</a>
|
<a href="/" class="button btn-secondary"><i class="fas fa-times"></i> Cancel</a>
|
||||||
{% if session['step'] != 1 %}
|
{% if session['step'] != 1 %}
|
||||||
<button class="button btn-secondary"><i class="fas fa-arrow-left"></i> Back</button>
|
<a href="/bot/create/back" class="button btn-secondary"><i class="fas fa-arrow-left"></i> Back</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if session['step'] < 5 %}
|
{% if session['step'] < 4 %}
|
||||||
<button class="button btn-primary"><i class="fas fa-arrow-right"></i> Next</button>
|
<button class="button btn-primary"><i class="fas fa-arrow-right"></i> Next</button>
|
||||||
{% else %}
|
{% else %}
|
||||||
<a href="/" class="button btn-primary"><i class="fas fa-check"></i> Finish</a>
|
<a href="/" class="button btn-primary"><i class="fas fa-check"></i> Finish</a>
|
||||||
|
|
|
@ -9,14 +9,14 @@
|
||||||
<body>
|
<body>
|
||||||
<div class="container centred">
|
<div class="container centred">
|
||||||
<h1 class="thin centred">Delete bot</h1>
|
<h1 class="thin centred">Delete bot</h1>
|
||||||
<div class="panel-icon"></div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="container centred">
|
<div class="container centred">
|
||||||
<form>
|
<form method='POST'>
|
||||||
|
<div class="panel-icon large"></div>
|
||||||
<div class="container centred">
|
<div class="container centred">
|
||||||
<p>Are you sure you want to <strong>permanently</strong> delete bot name?</p>
|
<p>Are you sure you want to <strong>permanently</strong> delete this bot?</p>
|
||||||
<p>The account on instan.ce will remain open, but your bot will stop posting from it.</p>
|
<p>The account on {{ instance }} will remain open, but your bot will stop posting from it.</p>
|
||||||
<a class="button btn-secondary" href="/"><i class="fas fa-times"></i> Cancel</a>
|
<a class="button btn-secondary" href="/"><i class="fas fa-times"></i> Cancel</a>
|
||||||
<button class="button btn-dangerous"><i class="fas fa-trash"></i> Delete bot</button>
|
<button class="button btn-dangerous"><i class="fas fa-trash"></i> Delete bot</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
19
templates/coming_soon.html
Normal file
19
templates/coming_soon.html
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>FediBooks</title>
|
||||||
|
{% include 'imports.html' %}
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="container centred light">
|
||||||
|
<h1 class="thin centred">Coming soon!</h1>
|
||||||
|
<div class="coming-soon"></div>
|
||||||
|
<br>
|
||||||
|
<a href="/" class="button btn-primary large"><i class="fas fa-home"></i> Home</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% include 'footer.html' %}
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -19,13 +19,14 @@
|
||||||
<div class="container" style="min-height: 300px;">
|
<div class="container" style="min-height: 300px;">
|
||||||
{% for bot in bots %}
|
{% for bot in bots %}
|
||||||
<div class="row light">
|
<div class="row light">
|
||||||
<div class="panel-icon {% if bot['enabled'] %}online{% else %}offline{% endif %}"></div>
|
<div class="panel-icon {{ "online" if bot['enabled'] else "offline"}}"></div>
|
||||||
<div class="panel-text">
|
<div class="panel-text">
|
||||||
<div class="panel-name">{{ bot['handle'] }}</div>
|
{% set handle_list = bot['handle'].split('@') %}
|
||||||
<div class="panel-status">{% if bot['enabled'] %}Online{% else %}Offline{% endif %}, learning from {{ bot_users[bot['id']] }} accounts</div>
|
<div class="panel-name">@{{ handle_list[1] }}<span class="subtle tiny">@{{ handle_list[2] }}</span></div>
|
||||||
|
<div class="panel-status">{{ "Online" if bot['enabled'] else "Offline"}}, learning from {{ bot_users[bot['handle']] }} accounts</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-actions">
|
<div class="panel-actions">
|
||||||
<a class="button btn-secondary" href="/bot/toggle/{{ bot['id'] }}" title="Turn on/off"><i class="fas fa-power-off"></i></a><a class="button btn-secondary" href="/bot/edit/{{ bot['id'] }}" title="Configure"><i class="fas fa-cog"></i></a><a class="button btn-secondary" href="/bot/accounts/{{ bot['id'] }}" title="Accounts learned from"><i class="fas fa-users"></i></a><a class="button btn-secondary" href="/bot/blacklist/{{ bot['id'] }}" title="Banned words"><i class="fas fa-strikethrough"></i></a><a class="button btn-secondary" href="/bot/chat/{{ bot['id'] }}" title="Chat"><i class="fas fa-comment"></i></a><a class="button btn-dangerous" href="/bot/delete/{{ bot['id'] }}" title="Delete"><i class="fas fa-trash"></i></a>
|
<a class="button btn-secondary" href="/bot/toggle/{{ bot['handle'] }}" title="Turn on/off"><i class="fas fa-power-off"></i></a><a class="button btn-secondary" href="/bot/edit/{{ bot['handle'] }}" title="Configure"><i class="fas fa-cog"></i></a><a class="button btn-secondary" href="/bot/accounts/{{ bot['handle'] }}" title="Accounts learned from"><i class="fas fa-users"></i></a><a class="button btn-secondary" href="/bot/blacklist/{{ bot['handle'] }}" title="Banned words"><i class="fas fa-strikethrough"></i></a><a class="button btn-secondary" href="/bot/chat/{{ bot['handle'] }}" title="Chat"><i class="fas fa-comment"></i></a><a class="button btn-dangerous" href="/bot/delete/{{ bot['handle'] }}" title="Delete"><i class="fas fa-trash"></i></a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<label for="email" class="large">Email address</label>
|
<label for="email" class="large">Email address</label>
|
||||||
<input type="email" name="email" value="me@my.site">
|
<input type="email" name="email" value="{{ user['email'] }}">
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<label for="password" class="large">Password</label>
|
<label for="password" class="large">Password</label>
|
||||||
|
@ -33,35 +33,35 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<label for="download-error" class="large">When my bot(s) can't get new posts</label>
|
<label for="fetch-error" class="large">When my bot(s) can't get new posts</label>
|
||||||
<select name="download-error">
|
<select name="fetch-error">
|
||||||
<option value="once">Once</option>
|
<option value="always" {{ 'selected' if user['fetch'] == "always"}}>Always</option>
|
||||||
<option value="every-time">Every time</option>
|
<option value="once" {{ 'selected' if user['fetch'] == "once"}}>Once</option>
|
||||||
<option value="never">Never</option>
|
<option value="never" {{ 'selected' if user['fetch'] == "never"}}>Never</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<label for="post-error" class="large">When my bot(s) can't submit new posts</label>
|
<label for="submit-error" class="large">When my bot(s) can't submit new posts</label>
|
||||||
<select name="post-error">
|
<select name="submit-error">
|
||||||
<option value="once">Once</option>
|
<option value="always" {{ 'selected' if user['submit'] == "always"}}>Always</option>
|
||||||
<option value="every-time">Every time</option>
|
<option value="once" {{ 'selected' if user['submit'] == "once"}}>Once</option>
|
||||||
<option value="never">Never</option>
|
<option value="never" {{ 'selected' if user['submit'] == "never"}}>Never</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<label for="generation-error" class="large">When my bot(s) encounter an error generating new posts</label>
|
<label for="generation-error" class="large">When my bot(s) encounter an error generating new posts</label>
|
||||||
<select name="generation-error">
|
<select name="generation-error">
|
||||||
<option value="once">Once</option>
|
<option value="always" {{ 'selected' if user['generation'] == "always"}}>Always</option>
|
||||||
<option value="every-time">Every time</option>
|
<option value="once" {{ 'selected' if user['generation'] == "once"}}>Once</option>
|
||||||
<option value="never">Never</option>
|
<option value="never" {{ 'selected' if user['generation'] == "never"}}>Never</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<label for="reply-error" class="large">When my bot(s) can't send replies</label>
|
<label for="reply-error" class="large">When my bot(s) can't send replies</label>
|
||||||
<select name="reply-error">
|
<select name="reply-error">
|
||||||
<option value="once">Once</option>
|
<option value="always" {{ 'selected' if user['reply'] == "always"}}>Always</option>
|
||||||
<option value="every-time">Every time</option>
|
<option value="once" {{ 'selected' if user['reply'] == "once"}}>Once</option>
|
||||||
<option value="never">Never</option>
|
<option value="never" {{ 'selected' if user['reply'] == "never"}}>Never</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
144
webui.py
144
webui.py
|
@ -1,4 +1,4 @@
|
||||||
from flask import Flask, render_template, session, request, redirect, url_for
|
from flask import Flask, render_template, session, request, redirect, url_for, send_file
|
||||||
from flask_mysqldb import MySQL
|
from flask_mysqldb import MySQL
|
||||||
from mastodon import Mastodon
|
from mastodon import Mastodon
|
||||||
import requests
|
import requests
|
||||||
|
@ -28,21 +28,22 @@ def home():
|
||||||
c.execute("SELECT COUNT(*) FROM `bots` WHERE user_id = %s", (session['user_id'],))
|
c.execute("SELECT COUNT(*) FROM `bots` WHERE user_id = %s", (session['user_id'],))
|
||||||
bot_count = c.fetchone()[0]
|
bot_count = c.fetchone()[0]
|
||||||
active_count = None
|
active_count = None
|
||||||
bots = None
|
bots = {}
|
||||||
bot_users = None
|
bot_users = None
|
||||||
|
|
||||||
if bot_count > 0:
|
if bot_count > 0:
|
||||||
c.execute("SELECT COUNT(*) FROM `bots` WHERE user_id = %s AND enabled = TRUE", (session['user_id'],))
|
c.execute("SELECT COUNT(*) FROM `bots` WHERE user_id = %s AND enabled = TRUE", (session['user_id'],))
|
||||||
active_count = c.fetchone()[0]
|
active_count = c.fetchone()[0]
|
||||||
dc = mysql.connection.cursor(MySQLdb.cursors.DictCursor)
|
dc = mysql.connection.cursor(MySQLdb.cursors.DictCursor)
|
||||||
dc.execute("SELECT * FROM `bots` WHERE user_id = %s", (session['user_id'],))
|
dc.execute("SELECT `handle`, `enabled` FROM `bots` WHERE user_id = %s", (session['user_id'],))
|
||||||
bots = dc.fetchall()
|
bots = dc.fetchall()
|
||||||
dc.close()
|
dc.close()
|
||||||
bot_users = {}
|
bot_users = {}
|
||||||
|
|
||||||
for bot in bots:
|
for bot in bots:
|
||||||
c.execute("SELECT COUNT(*) FROM `bot_learned_accounts` WHERE bot_id = %s", (bot['id'],))
|
# multiple SELECTS is slow, maybe SELECT all at once and filter with python?
|
||||||
bot_users[bot['id']] = c.fetchone()[0]
|
c.execute("SELECT COUNT(*) FROM `bot_learned_accounts` WHERE bot_id = %s", (bot['handle'],))
|
||||||
|
bot_users[bot['handle']] = c.fetchone()[0]
|
||||||
|
|
||||||
c.close()
|
c.close()
|
||||||
return render_template("home.html", bot_count = bot_count, active_count = active_count, bots = bots, bot_users = bot_users)
|
return render_template("home.html", bot_count = bot_count, active_count = active_count, bots = bots, bot_users = bot_users)
|
||||||
|
@ -68,24 +69,122 @@ def show_signup_page(error = None):
|
||||||
|
|
||||||
@app.route("/settings")
|
@app.route("/settings")
|
||||||
def settings():
|
def settings():
|
||||||
return render_template("settings.html")
|
return render_template("coming_soon.html")
|
||||||
|
dc = mysql.connection.cursor(MySQLdb.cursors.DictCursor)
|
||||||
|
dc.execute("SELECT * FROM `users` WHERE id = %s", (session['user_id'],))
|
||||||
|
user = dc.fetchone()
|
||||||
|
dc.close()
|
||||||
|
return render_template("settings.html", user = user)
|
||||||
|
|
||||||
@app.route("/bot/edit/<id>")
|
@app.route("/bot/edit/<id>")
|
||||||
def bot_edit(id):
|
def bot_edit(id):
|
||||||
return render_template("bot_edit.html")
|
return render_template("coming_soon.html")
|
||||||
|
|
||||||
@app.route("/bot/delete/<id>")
|
@app.route("/bot/delete/<id>", methods=['GET', 'POST'])
|
||||||
def bot_delete(id):
|
def bot_delete(id):
|
||||||
return render_template("bot_delete.html")
|
if bot_check(id):
|
||||||
|
if request.method == 'GET':
|
||||||
|
instance = id.split("@")[2]
|
||||||
|
return render_template("bot_delete.html", instance = instance)
|
||||||
|
else:
|
||||||
|
# delete bot by deleting its credentials
|
||||||
|
# FK constraint will delete bot
|
||||||
|
c = mysql.connection.cursor()
|
||||||
|
c.execute("SELECT `credentials_id` FROM `bots` WHERE `handle` = %s", (id,))
|
||||||
|
credentials_id = c.fetchone()[0]
|
||||||
|
c.execute("DELETE FROM `credentials` WHERE `id` = %s", (credentials_id,))
|
||||||
|
c.close()
|
||||||
|
mysql.connection.commit()
|
||||||
|
|
||||||
|
return redirect(url_for("home"), 303)
|
||||||
|
|
||||||
|
@app.route("/bot/toggle/<id>")
|
||||||
|
def bot_toggle(id):
|
||||||
|
if bot_check(id):
|
||||||
|
c = mysql.connection.cursor()
|
||||||
|
c.execute("UPDATE `bots` SET `enabled` = NOT `enabled` WHERE `handle` = %s", (id,))
|
||||||
|
mysql.connection.commit()
|
||||||
|
c.close()
|
||||||
|
return redirect(url_for("home"), 303)
|
||||||
|
|
||||||
|
@app.route("/bot/chat/<id>")
|
||||||
|
def bot_chat(id):
|
||||||
|
return render_template("coming_soon.html")
|
||||||
|
|
||||||
|
@app.route("/bot/blacklist/<id>")
|
||||||
|
def bot_blacklist(id):
|
||||||
|
return render_template("coming_soon.html")
|
||||||
|
|
||||||
@app.route("/bot/accounts/<id>")
|
@app.route("/bot/accounts/<id>")
|
||||||
def bot_accounts(id):
|
def bot_accounts(id):
|
||||||
return render_template("bot_accounts.html")
|
if bot_check(id):
|
||||||
|
session['bot'] = id
|
||||||
|
c = mysql.connection.cursor()
|
||||||
|
c.execute("SELECT COUNT(*) FROM `bot_learned_accounts` WHERE `bot_id` = %s", (id,))
|
||||||
|
user_count = c.fetchone()[0]
|
||||||
|
users = {}
|
||||||
|
|
||||||
@app.route("/bot/accounts/add")
|
if user_count > 0:
|
||||||
|
dc = mysql.connection.cursor(MySQLdb.cursors.DictCursor)
|
||||||
|
dc.execute("SELECT `fedi_id`, `enabled` FROM `bot_learned_accounts` WHERE `bot_id` = %s", (id,))
|
||||||
|
users = dc.fetchall()
|
||||||
|
dc.close()
|
||||||
|
|
||||||
|
c.close()
|
||||||
|
|
||||||
|
return render_template("bot_accounts.html", users = users)
|
||||||
|
|
||||||
|
@app.route("/bot/accounts/add", methods = ['GET', 'POST'])
|
||||||
def bot_accounts_add():
|
def bot_accounts_add():
|
||||||
|
if request.method == 'POST':
|
||||||
|
if session['step'] == 1:
|
||||||
|
# look up user
|
||||||
|
handle_list = request.form['account'].split('@')
|
||||||
|
username = handle_list[1]
|
||||||
|
instance = handle_list[2]
|
||||||
|
|
||||||
|
# 1. download host-meta to find webfinger URL
|
||||||
|
r = requests.get("https://{}/.well-known/host-meta".format(instance), timeout=10)
|
||||||
|
# 2. use webfinger to find user's info page
|
||||||
|
#TODO: use more reliable method
|
||||||
|
uri = re.search(r'template="([^"]+)"', r.text).group(1)
|
||||||
|
uri = uri.format(uri = "{}@{}".format(username, instance))
|
||||||
|
r = requests.get(uri, headers={"Accept": "application/json"}, timeout=10)
|
||||||
|
j = r.json()
|
||||||
|
found = False
|
||||||
|
for link in j['links']:
|
||||||
|
if link['rel'] == 'self':
|
||||||
|
#this is a link formatted like "https://instan.ce/users/username", which is what we need
|
||||||
|
uri = link['href']
|
||||||
|
found = True
|
||||||
|
break
|
||||||
|
if not found:
|
||||||
|
return "Couldn't find a valid ActivityPub outbox URL."
|
||||||
|
|
||||||
|
# 3. format as outbox URL and check to make sure it works
|
||||||
|
outbox = "{}/outbox?page=true".format(uri)
|
||||||
|
r = requests.get(uri, headers={"Accept": "application/json"}, timeout=10)
|
||||||
|
if r.status_code == 200:
|
||||||
|
# success!!
|
||||||
|
c = mysql.connection.cursor()
|
||||||
|
c.execute("INSERT INTO `fedi_accounts` (`handle`, `outbox`) VALUES (%s, %s)", (request.form['account'], outbox))
|
||||||
|
c.execute("INSERT INTO `bot_learned_accounts` (`bot_id`, `fedi_id`) VALUES (%s, %s)", (session['bot'], request.form['account']))
|
||||||
|
c.close()
|
||||||
|
mysql.connection.commit()
|
||||||
|
|
||||||
|
return redirect("/bot/accounts/{}".format(session['bot']), 303)
|
||||||
|
|
||||||
return render_template("bot_accounts_add.html")
|
return render_template("bot_accounts_add.html")
|
||||||
|
|
||||||
|
@app.route("/bot/accounts/toggle/<id>")
|
||||||
|
def bot_accounts_toggle(id):
|
||||||
|
c = mysql.connection.cursor()
|
||||||
|
c.execute("UPDATE `bot_learned_accounts` SET `enabled` = NOT `enabled` WHERE `fedi_id` = %s AND `bot_id` = %s", (id, session['bot']))
|
||||||
|
mysql.connection.commit()
|
||||||
|
c.close()
|
||||||
|
return redirect("/bot/accounts/{}".format(session['bot']), 303)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/bot/create/", methods=['GET', 'POST'])
|
@app.route("/bot/create/", methods=['GET', 'POST'])
|
||||||
def bot_create():
|
def bot_create():
|
||||||
#TODO: error handling
|
#TODO: error handling
|
||||||
|
@ -95,7 +194,7 @@ def bot_create():
|
||||||
session['instance'] = re.match(r"^(?:https?:\/\/)?(.*)", request.form['instance']).group(1)
|
session['instance'] = re.match(r"^(?:https?:\/\/)?(.*)", request.form['instance']).group(1)
|
||||||
|
|
||||||
# check for mastodon/pleroma
|
# check for mastodon/pleroma
|
||||||
r = requests.get("https://{}/api/v1/instance".format(session['instance']))
|
r = requests.get("https://{}/api/v1/instance".format(session['instance']), timeout=10)
|
||||||
if r.status_code == 200:
|
if r.status_code == 200:
|
||||||
j = r.json()
|
j = r.json()
|
||||||
if "Pleroma" in j['version']:
|
if "Pleroma" in j['version']:
|
||||||
|
@ -155,7 +254,8 @@ def bot_create():
|
||||||
session['step'] = 1
|
session['step'] = 1
|
||||||
return bot_create()
|
return bot_create()
|
||||||
|
|
||||||
elif session['step'] == 4:
|
else:
|
||||||
|
if session['step'] == 4:
|
||||||
try:
|
try:
|
||||||
# test authentication
|
# test authentication
|
||||||
client = Mastodon(client_id=session['client_id'], client_secret=session['client_secret'], api_base_url=session['instance'])
|
client = Mastodon(client_id=session['client_id'], client_secret=session['client_secret'], api_base_url=session['instance'])
|
||||||
|
@ -172,10 +272,8 @@ def bot_create():
|
||||||
credentials_id = c.lastrowid
|
credentials_id = c.lastrowid
|
||||||
mysql.connection.commit()
|
mysql.connection.commit()
|
||||||
|
|
||||||
bot_id = hashlib.sha256(handle.encode('utf-8')).digest()
|
c.execute("INSERT INTO `bots` (handle, user_id, credentials_id) VALUES (%s, %s, %s)", (handle, session['user_id'], credentials_id))
|
||||||
c.execute("INSERT INTO `bots` (id, user_id, credentials_id, handle) VALUES (%s, %s, %s, %s)", (bot_id, session['user_id'], credentials_id, handle))
|
|
||||||
mysql.connection.commit()
|
mysql.connection.commit()
|
||||||
|
|
||||||
c.close()
|
c.close()
|
||||||
|
|
||||||
# clean up unneeded variables
|
# clean up unneeded variables
|
||||||
|
@ -187,6 +285,11 @@ def bot_create():
|
||||||
|
|
||||||
return render_template("bot_create.html")
|
return render_template("bot_create.html")
|
||||||
|
|
||||||
|
@app.route("/bot/create/back")
|
||||||
|
def bot_create_back():
|
||||||
|
session['step'] -= 1
|
||||||
|
return redirect(url_for("bot_create"), 303)
|
||||||
|
|
||||||
@app.route("/do/authenticate_bot")
|
@app.route("/do/authenticate_bot")
|
||||||
def do_authenticate_bot():
|
def do_authenticate_bot():
|
||||||
session['code'] = request.args.get('code')
|
session['code'] = request.args.get('code')
|
||||||
|
@ -235,3 +338,12 @@ def do_login():
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return "invalid login"
|
return "invalid login"
|
||||||
|
|
||||||
|
@app.route("/img/bot_generic.png")
|
||||||
|
def img_bot_generic():
|
||||||
|
return send_file("static/bot_generic.png", mimetype="image/png")
|
||||||
|
|
||||||
|
def bot_check(bot):
|
||||||
|
c = mysql.connection.cursor()
|
||||||
|
c.execute("SELECT COUNT(*) FROM `bots` WHERE `handle` = %s AND `user_id` = %s", (bot, session['user_id']))
|
||||||
|
return c.fetchone()[0] == 1
|
||||||
|
|
Loading…
Reference in a new issue