Compare commits

...

7 Commits

7 changed files with 125 additions and 3 deletions

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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>