mirror of https://gitlab.com/bashrc2/epicyon
Compare commits
7 Commits
aa1e481b10
...
6dbf97e172
Author | SHA1 | Date |
---|---|---|
Bob Mottram | 6dbf97e172 | |
Bob Mottram | a7c94a006a | |
Bob Mottram | 94c2bd819e | |
Bob Mottram | 0e7490a44b | |
Bob Mottram | 9b75c034a0 | |
Bob Mottram | ec5e7bc59f | |
Bob Mottram | 50c44d6b7e |
|
@ -1,5 +1,7 @@
|
|||
<blockquote><b>Epicyon</b>, meaning <i>"more than a dog"</i>. Largest of the <i>Borophaginae</i> which lived in North America 20-5 million years ago.</blockquote>
|
||||
|
||||
<img src="https://code.freedombone.net/bashrc/epicyon/raw/master/img/screenshot_indymedia.jpg?raw=true" width="80%"/>
|
||||
|
||||
<img src="https://code.freedombone.net/bashrc/epicyon/raw/master/img/mobile.jpg?raw=true" width="30%"/>
|
||||
|
||||
Epicyon is a modern [ActivityPub](https://www.w3.org/TR/activitypub) compliant server implementing both S2S and C2S protocols and sutable for installation on single board computers. It includes features such as moderation tools, post expiry, content warnings, image descriptions and perimeter defense against adversaries. It contains *no javascript* and uses HTML+CSS with a Python backend.
|
||||
|
|
|
@ -67,6 +67,7 @@
|
|||
--quote-font-size: 120%;
|
||||
--line-spacing: 130%;
|
||||
--line-spacing-newswire: 100%;
|
||||
--newswire-moderate-color: yellow;
|
||||
--column-left-width: 10vw;
|
||||
--column-center-width: 80vw;
|
||||
--column-right-width: 10vw;
|
||||
|
@ -231,6 +232,13 @@ a:focus {
|
|||
line-height: var(--line-spacing-newswire);
|
||||
}
|
||||
|
||||
.newswireItemModerate {
|
||||
font-size: var(--font-size-newswire);
|
||||
color: var(--newswire-moderate-color);
|
||||
font-weight: bold;
|
||||
line-height: var(--line-spacing-newswire);
|
||||
}
|
||||
|
||||
.newswireDate {
|
||||
font-size: var(--font-size-newswire);
|
||||
color: var(--newswire-date-color);
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 83 KiB |
Binary file not shown.
Before Width: | Height: | Size: 124 KiB After Width: | Height: | Size: 83 KiB |
101
newswire.py
101
newswire.py
|
@ -13,6 +13,8 @@ from socket import error as SocketError
|
|||
import errno
|
||||
from datetime import datetime
|
||||
from collections import OrderedDict
|
||||
from utils import locatePost
|
||||
from utils import loadJson
|
||||
|
||||
|
||||
def rss2Header(httpPrefix: str,
|
||||
|
@ -162,6 +164,99 @@ def getRSSfromDict(baseDir: str, newswire: {},
|
|||
return rssStr
|
||||
|
||||
|
||||
def addAccountBlogsToNewswire(baseDir: str, nickname: str, domain: str,
|
||||
newswire: {},
|
||||
maxBlogsPerAccount: int,
|
||||
indexFilename: str) -> None:
|
||||
"""Adds blogs for the given account to the newswire
|
||||
"""
|
||||
if not os.path.isfile(indexFilename):
|
||||
return
|
||||
with open(indexFilename, 'r') as indexFile:
|
||||
postFilename = 'start'
|
||||
ctr = 0
|
||||
while postFilename:
|
||||
postFilename = indexFile.readline()
|
||||
if postFilename:
|
||||
# if this is a full path then remove the directories
|
||||
if '/' in postFilename:
|
||||
postFilename = postFilename.split('/')[-1]
|
||||
|
||||
# filename of the post without any extension or path
|
||||
# This should also correspond to any index entry in
|
||||
# the posts cache
|
||||
postUrl = \
|
||||
postFilename.replace('\n', '').replace('\r', '')
|
||||
postUrl = postUrl.replace('.json', '').strip()
|
||||
|
||||
# read the post from file
|
||||
fullPostFilename = \
|
||||
locatePost(baseDir, nickname,
|
||||
domain, postUrl, False)
|
||||
isAPost = False
|
||||
postJsonObject = None
|
||||
if fullPostFilename:
|
||||
postJsonObject = loadJson(fullPostFilename)
|
||||
if postJsonObject:
|
||||
if postJsonObject.get('object'):
|
||||
if isinstance(postJsonObject['object'], dict):
|
||||
isAPost = True
|
||||
if isAPost:
|
||||
if postJsonObject['object'].get('summary') and \
|
||||
postJsonObject['object'].get('url') and \
|
||||
postJsonObject['object'].get('published'):
|
||||
published = postJsonObject['object']['published']
|
||||
published = published.replace('T', ' ')
|
||||
published = published.replace('Z', '+00:00')
|
||||
newswire[published] = \
|
||||
[postJsonObject['object']['summary'],
|
||||
postJsonObject['object']['url']]
|
||||
|
||||
ctr += 1
|
||||
if ctr >= maxBlogsPerAccount:
|
||||
break
|
||||
|
||||
|
||||
def addLocalBlogsToNewswire(baseDir: str, newswire: {},
|
||||
maxBlogsPerAccount: int) -> None:
|
||||
"""Adds blogs from this instance into the newswire
|
||||
"""
|
||||
suspendedFilename = baseDir + '/accounts/suspended.txt'
|
||||
# go through each account
|
||||
for subdir, dirs, files in os.walk(baseDir + '/accounts'):
|
||||
for handle in dirs:
|
||||
if '@' not in handle:
|
||||
continue
|
||||
if 'inbox@' in handle:
|
||||
continue
|
||||
accountDir = os.path.join(baseDir + '/accounts', handle)
|
||||
|
||||
# has this account been suspended?
|
||||
nickname = handle.split('@')[0]
|
||||
if os.path.isfile(suspendedFilename):
|
||||
with open(suspendedFilename, "r") as f:
|
||||
lines = f.readlines()
|
||||
foundSuspended = False
|
||||
for nick in lines:
|
||||
if nick == nickname + '\n':
|
||||
foundSuspended = True
|
||||
break
|
||||
if foundSuspended:
|
||||
continue
|
||||
|
||||
# has this account been blocked from posting to newswire?
|
||||
if os.path.isfile(accountDir + '/.noblognewswire'):
|
||||
continue
|
||||
|
||||
# is there a blogs timeline for this account?
|
||||
blogsIndex = accountDir + '/tlblogs.index'
|
||||
if os.path.isfile(blogsIndex):
|
||||
domain = handle.split('@')[1]
|
||||
addAccountBlogsToNewswire(baseDir, nickname, domain,
|
||||
newswire, maxBlogsPerAccount,
|
||||
blogsIndex)
|
||||
|
||||
|
||||
def getDictFromNewswire(session, baseDir: str) -> {}:
|
||||
"""Gets rss feeds as a dictionary from newswire file
|
||||
"""
|
||||
|
@ -169,6 +264,7 @@ def getDictFromNewswire(session, baseDir: str) -> {}:
|
|||
if not os.path.isfile(subscriptionsFilename):
|
||||
return {}
|
||||
|
||||
# add rss feeds
|
||||
rssFeed = []
|
||||
with open(subscriptionsFilename, 'r') as fp:
|
||||
rssFeed = fp.readlines()
|
||||
|
@ -182,6 +278,11 @@ def getDictFromNewswire(session, baseDir: str) -> {}:
|
|||
itemsList = getRSS(session, url)
|
||||
for dateStr, item in itemsList.items():
|
||||
result[dateStr] = item
|
||||
|
||||
# add local content
|
||||
addLocalBlogsToNewswire(baseDir, result, 5)
|
||||
|
||||
# sort into chronological order, latest first
|
||||
sortedResult = OrderedDict(sorted(result.items(), reverse=True))
|
||||
return sortedResult
|
||||
|
||||
|
|
|
@ -5342,8 +5342,19 @@ def htmlNewswire(newswire: str) -> str:
|
|||
"""
|
||||
htmlStr = ''
|
||||
for dateStr, item in newswire.items():
|
||||
htmlStr += '<p class="newswireItem">' + \
|
||||
'<a href="' + item[1] + '">' + item[0] + '</a>'
|
||||
# if the item is to be moderated then show it in a different style
|
||||
shown = False
|
||||
if len(item) > 2:
|
||||
if item[2].startswith('moderate'):
|
||||
moderationUrl = '/moderate?' + item[1]
|
||||
htmlStr += '<p class="newswireItemModerate">' + \
|
||||
'<a href="' + moderationUrl + '">' + item[0] + '</a>'
|
||||
shown = True
|
||||
|
||||
if not shown:
|
||||
# unmoderated item
|
||||
htmlStr += '<p class="newswireItem">' + \
|
||||
'<a href="' + item[1] + '">' + item[0] + '</a>'
|
||||
htmlStr += ' <label class="newswireDate">'
|
||||
htmlStr += dateStr.replace('+00:00', '') + '</label></p>'
|
||||
return htmlStr
|
||||
|
|
|
@ -1168,7 +1168,7 @@
|
|||
<th><a href="img/screenshot_hacker.jpg"><img width="90%" src="img/screenshot_hacker.jpg" alt="hacker theme profile page" /></a></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><img width="90%" src="img/screenshots.jpg" alt="various screenshots" /></th>
|
||||
<th><a href="img/screenshot_indymedia.jpg"><img width="120%" src="img/screenshots.jpg" alt="various screenshots" /></a></th>
|
||||
<th><img width="50%" src="img/mobile.jpg" alt="mobile screenshot" /></th>
|
||||
</tr>
|
||||
<tr>
|
||||
|
|
Loading…
Reference in New Issue