Merge branch 'master' of github.com:modgethanc/ttbp

endorphant 2018-03-30 22:11:21 -04:00
commit 198bde8ed3
11 changed files with 1490 additions and 353 deletions

View File

@ -1,160 +1,204 @@
<p><em>a command-line based blogging platform running on tilde.town</em></p> <p><em>a command-line based blogging platform running on tilde.town</em></p>
<p><code>ttbp</code> stands for "tilde.town blogging platform", the original working name for <p><code>ttbp</code> stands for "tilde.town blogging platform", the original working name for
this project.</p> this project.</p>
<p><img alt="ttbp main menu screenshot" src="http://tilde.town/~endorphant/ttbp/screenshots/ttbp-main.png" /></p>
<p><img src="http://tilde.town/~endorphant/ttbp/screenshots/ttbp-main.png" alt="ttbp main menu screenshot" /></p> <p><img alt="ttbp entries view screenshot" src="http://tilde.town/~endorphant/ttbp/screenshots/ttbp-entries.png" /></p>
<p><img alt="ttbp compose view screenshot" src="http://tilde.town/~endorphant/ttbp/screenshots/ttbp-compose.png" /></p>
<p><img src="http://tilde.town/~endorphant/ttbp/screenshots/ttbp-entries.png" alt="ttbp entries view screenshot" /></p>
<p><img src="http://tilde.town/~endorphant/ttbp/screenshots/ttbp-compose.png" alt="ttbp compose view screenshot" /></p>
<p><code>ttbp</code> runs from the command line, providing a hub for writing personal blog <p><code>ttbp</code> runs from the command line, providing a hub for writing personal blog
posts and reading posts written by other users of tilde.town. it's a little bit posts and reading posts written by other users of tilde.town. it's a little bit
like livejournal or dreamwidth or tumblr. you can opt to publish your posts to like livejournal or dreamwidth or tumblr. you can opt to publish your posts to a
a public html file hosted on your tilde page, or keep all your entries private public html file hosted on your tilde page, to tilde.town's gopher server, or
to the tilde.town server.</p> keep all your entries private to the tilde.town server.</p>
<p>to use, run <code>feels</code> while logged in to tilde.town</p> <p>to use, run <code>feels</code> while logged in to tilde.town</p>
<p>this is a project that runs on tilde.town, so all users of this program are
<p>(<code>feels</code> is a tilde.town specific command; if you're running this locally, or on expected to operate under the tilde.town <a href="http://tilde.town/wiki/conduct.html">code of
a different server, run <code>ttbp</code> from the command line)</p> conduct</a>. content/personal issues should be
worked out according to the CoC, with support from the <a href="http://tilde.town/wiki/administration/index.html">administrative
<h3 id="quickstart">QUICK START</h3> team</a> if needed.</p>
<h3>QUICK START</h3>
<p>no coding or html experience is necessary to get started. just log in to your <p>no coding or html experience is necessary to get started. just log in to your
tilde.town account and enter:</p> tilde.town account and enter:</p>
<p><code>feels</code></p> <p><code>feels</code></p>
<p>ttbp will ask you a few questions to get you started. after that, writing and <p>ttbp will ask you a few questions to get you started. after that, writing and
reading entries all happen within the program.</p> reading entries all happen within the program.</p>
<p>that's it!</p> <p>that's it!</p>
<h3>support</h3>
<h3 id="support">SUPPORT</h3>
<p>if you're having trouble getting started, or run into program errors or strange <p>if you're having trouble getting started, or run into program errors or strange
behavior, please send internal tilde.town mail to ~endorphant and i'll try to behavior, please send internal tilde.town mail to ~endorphant and i'll try to
sort things out!</p> sort things out!</p>
<p>there's also a function from the main menu that lets you send feedback/inquiries
<h3 id="writingentries">writing entries</h3> to me directly; this uses internal tilde.town mail, which is what i'll respond
to.</p>
<p>entries are recorded as plaintext files in your ~/.ttbp/entries <h3>writing entries</h3>
directory. you can edit them there directly, or fix old entries, or <p>entries are recorded as plaintext files in your <code>~/.ttbp/entries</code> directory.
delete entries.</p> <code>ttbp</code> will use your selected editor to open and write files; each day is its
own entry, like a diary page. at midnight for whatever timezone you've set for
<p><em>warning</em>: changing old entries might cause strange things to your user account on tilde.town, you'll get a fresh entry. if you don't write
happen with timestamps. the main program looks at the filename any feels on a particular day, no entries will show up there.</p>
first for setting the date, then the last modified time to sort <p>when you save and quit the text editor, your entry will automatically propagate
recent posts. it expects YYYMMDD.txt as the filename; anything else to the global feels list; if you've opted to publish your feels to html/gopher,
won't show up as a valid entry. yes, this means you can post things out those files will update immediately. you can always go back to the current day's
of date order by creating files with any date you want.</p> entry and edit/add as you'd like, but older entries will not be available for
editing from <code>ttbp</code>.</p>
<h4 id="generalentrywritingnotes">general entry-writing notes</h4> <p><em>(since files are just stored as plaintext in your directory, it's possible to
edit and move old entries directly from the command line. however, changing old
entries might cause strange things to happen with timestamps. the main program
looks at the filename first for setting the date, then the last modified time to
sort recent posts. it expects YYYMMDD.txt as the filename; anything else won't
show up as a valid entry. yes, this means you can post things out of date order
by creating files with any date you want.)</em></p>
<h4>general entry-writing notes</h4>
<ul> <ul>
<li>you can use <a href="https://daringfireball.net/projects/markdown/syntax">markdown</a></li> <li>you can use <a href="https://daringfireball.net/projects/markdown/syntax">markdown</a></li>
<li>you can use html</li> <li>you can use html</li>
<li>you can also put things between <code>&lt;!-- comments --&gt;</code> to have them show up <li>you can also put things between <code>&lt;!-- comments --&gt;</code> to have them show up
in the feed but not render in a browser (but people can still read in the feed but not render in a browser (but people can still read
them with view-source)</li> them with view-source)</li>
</ul> </ul>
<h3>reading other feels</h3>
<h3 id="privacy">privacy</h3> <p>the <code>browse global feels</code> feature shows the ten most recent entries that anyone
has written on ttbp. this list is only accessible from within tilde.town,
although individual entries may be posted to html or gopher.</p>
<p>you can also pull up a list of a single user's feels through <code>check out your
neighbors</code>, which displays all users who are writing on <code>ttbp</code> based on their
most recently updated entry, and a link to their public html blog if they've
opted to publish their posts.</p>
<p><strong>please note!</strong> entries written on <code>ttbp</code> should be considered sensitive,
private information, even if a particular user is publishing entries in a
world-viewable way! please be respectful about having access to other people's
feels, and do not copy/repeat any information without getting their explicit
permission. tilde.town operates on a high level of mutual trust, and <code>ttbp</code> is
designed to give individuals control over their content.</p>
<h3>privacy</h3>
<p>when you start your ttbp, you have the option of publishing or not publishing <p>when you start your ttbp, you have the option of publishing or not publishing
your blog.</p> your blog.</p>
<p>if you opt to not publish, your entires will never be accessible from outside of
<p>if you opt to publish, the program creates a directory <code>~/.ttbp/www</code> the tilde.town network; other tilde.town users will still be able to read your
where it stores all html files it generates, and symlinks this from your entries through the ttbp interface, or by directly accessing your
<code>~/public_html</code> with your chosen blog directory. your blog will also be listed
on the <a href="https://tilde.town/~endorphant/ttbp">main ttbp page</a>.</p>
<p>if you opt to not publish, your entires will never be accessible from outside
of the tilde.town network; other tilde.town users will still be able to read
your entries through the ttbp interface, or by directly accessing your
<code>~/.ttbp/entries</code> directory.</p> <code>~/.ttbp/entries</code> directory.</p>
<p>if you want to further protect your entries, you can <code>chmod 700</code> your entries <p>if you want to further protect your entries, you can <code>chmod 700</code> your entries
directory.</p> directory.</p>
<p>if you opt to publish, the program creates a directory <code>~/.ttbp/www</code> where it
<h3 id="changingyourpagelayout">changing your page layout</h3> stores all html files it generates, and symlinks this from your <code>~/public_html</code>
with your chosen blog directory. your blog will also be listed on the <a href="https://tilde.town/~endorphant/ttbp">main ttbp
<p>you can modify how your blog looks by editing the stylesheet or page</a>.</p>
header and footer files. the program sets you up with basic <p>you can also opt to publish to gopher, and the program will automatically
default. if you break your page somehow, you can force the program to generate a gophermap of your feels.</p>
regenerate your configuration by deleting your ~/.ttbp directory entirely. <p>you can set publishing status on individual entries, or bury individual feels;
<strong>you might want to back up your ~/.ttbp/entries directory before you do see "data management" below for details.</p>
this.</strong></p> <h3>data management</h3>
<p>the <code>manage your feels</code> menu provides several tools for organizing your feels.
these are all actions you can perform manually from the command line, but doing
them from within the program can help keep your files properly linked up.</p>
<ul> <ul>
<li>to modify your stylesheet, edit your ~/.ttbp/config/style.css <li><strong>read over feels</strong>--a list of all your entries, which you can open and
read like any other feel</li>
<li><strong>modify feels publishing</strong>--this lets you toggle privacy on individual
posts. entries marked <code>(nopub)</code> will not get written to html or gopher,
and toggling them from this menu will immediately publish or unpublish
that entry (if you're not publishing your posts at all, these settings
won't matter, since your feels will never show up outside of tilde.town)</li>
<li><strong>backup your feels</strong>--makes a .tar.gz of all your entries, saving one
copy to <code>~/.ttbp/backups/</code> with the current date, and a second copy to
your home directory for safekeeping.</li>
<li><strong>import a feels backup</strong>--unpacks a backup file into your current feels
list. this tool checks the <code>~/.ttbp/backups</code> directory for archives, and
expects a file created by the above backup utility. if it detects any file
collisions, it will preserve your current live copy and leave the backup
verison in a temp directory, and notify you that this happened. also, any
entries that were previously marked as <code>(nopub)</code> will retain their nopub
status.</li>
<li><strong>bury some feels</strong>--hides individual feels from viewing; entries are
moved to <code>~/.ttbp/buried</code> (and marked with a unique timestamp to prevent
file collision) with permissions set to 600, meaning no one except you
will be able to open that file. these entries are also hidden from your
own view from <code>read over feels</code>, and you'll have to open the files from
the command line if you want to see them. this is intended to be a
permament action, so you'll be asked to type the entry date once to load
the feel, then shown a preview of that feel, and then type the date again
to confirm burying.</li>
<li><strong>delete feels by day</strong>--<em>permanently removes individual entries</em>,
including deleting published html/gopher files if needed. this action is
not recoverable, unless you have a backup to restore; you'll be asked to
type the entry date once to load the feel, then shown a preview of that
feel, and then type the date again to confirm deletion.</li>
<li><strong>purge all feels</strong>--<em>permanently removes all feels</em>, including deleting
all published html/gopher files if needed. this action is not recoverable,
unless you have a backup to restore. you'll be asked to type a
one-time-use purge code to confirm this action.</li>
<li><strong>wipe feels account</strong>--<em>permanently removes all data associated with
feels</em>, including deleting any published hmtl/gopher files and removing
your <code>~/.ttbp</code> directory. any backups that you have in <code>~/.ttbp/backups</code>
will also be deleted with this action (which is why the backup function
makes a second copy for safekeeping in your home directory). you will no
longer show up in any lists as a user.</li>
</ul>
<h3>settings</h3>
<p>the settings menu lets you change specific options for handling your feels and
using the interface.</p>
<ul> <ul>
<li>(future feature: having multiple stylesheets you can select)</li></ul> <li><strong>editor</strong>--set your text editor</li>
</li> <li><strong>gopher</strong>--opt in or out of automatically posting to gopher</li>
<li><strong>post as nopub</strong>--set whether posts default to being published or not
<li>to modify the page header, edit your ~/.ttbp/config/header.txt published (if you're not publishing your feels, this doesn't matter)</li>
<li><strong>publish dir</strong>--set the directory under you <code>public_html</code> where feels will be
published (if you're not publishing your feels, this defaults to <code>None</code>)</li>
<li><strong>publishing</strong>--opt in or out of automatically publishing entries to a
world-readable html page</li>
<li><strong>rainbows</strong>--opt in or out of having multicolored menu text</li>
</ul>
<h3>changing your page layout</h3>
<p>you can modify how your blog looks by editing the stylesheet or header and
footer files. the program sets you up with basic default. if you break your page
somehow, you can force the program to regenerate your configuration by deleting
your ~/.ttbp directory entirely. <strong>you might want to back up your
~/.ttbp/entries directory before you do this.</strong></p>
<ul> <ul>
<li>you might note that there's a place marked off in the default header where <li>to modify your stylesheet, edit your ~/.ttbp/config/style.css</li>
you can safely put custom HTML elements!</li></ul> <li>to modify the page header, edit your ~/.ttbp/config/header.txt</li>
</li> <li>there's a place marked off in the default header where you can safely put
custom HTML elements!</li>
<li>to modify the page footer, edit your ~/.ttbp/config/footer.txt</li> <li>to modify the page footer, edit your ~/.ttbp/config/footer.txt</li>
</ul> </ul>
<h3>general tips/troubleshooting</h3>
<h3 id="generaltipstroubleshooting">general tips/troubleshooting</h3>
<ul> <ul>
<li>add <code>alias ttbp="~endorphant/bin/ttbp"</code> to your .bash_aliases for fewer keystrokes</li>
<li>(similarly, <code>alias ttbp-beta="~endorphant/bin/ttbp-beta"</code>)</li>
<li>if the date looks like it's ahead or behind, it's because you haven't set <li>if the date looks like it's ahead or behind, it's because you haven't set
your local timezone yet. here are some your local timezone yet. here are some
<a href="http://www.cyberciti.biz/faq/linux-unix-set-tz-environment-variable/">timezone setting instructions</a></li> <a href="http://www.cyberciti.biz/faq/linux-unix-set-tz-environment-variable/">timezone setting instructions</a></li>
<li>the feels burying tool will effectively clear your post for the day; you can
use this feature to start a fresh entry on a particular day by burying the
current day's feels and then editing a new file</li>
</ul> </ul>
<h3>future features</h3>
<h3 id="futurefeatures">future features</h3>
<p>these are a few ideas being kicked around, or under active development:</p> <p>these are a few ideas being kicked around, or under active development:</p>
<ul> <ul>
<li>better entry privacy/publish control options</li>
<li>stylesheet/theme selector</li> <li>stylesheet/theme selector</li>
<li>better entry display within ttbp (currently just offloads to <code>less</code>)</li>
<li>responding to entries</li> <li>buried feels browser</li>
<li>paginated list view</li>
<li>better entry display within ttbp</li>
</ul> </ul>
<p>other ideas are listed on github as
<h3 id="dependencies">dependencies</h3> <a href="https://github.com/modgethanc/ttbp/issues?q=is%3Aissue+is%3Aopen+label%3A" title="upcoming+features">upcoming features</a> or <a href="https://github.com/modgethanc/ttbp/issues?q=is%3Aissue+is%3Aopen+label%3A" title="feature+request">feature requests</a>!</p>
<h3>dependencies</h3>
<p>(this section is only relevant if you plan on forking the repo and running an <p>(this section is only relevant if you plan on forking the repo and running an
instance of this yourself)</p> instance of this yourself)</p>
<ul> <ul>
<li><a href="https://pypi.python.org/pypi/mistune">mistune</a></li> <li><a href="https://pypi.python.org/pypi/mistune">mistune</a></li>
<li><a href="https://pypi.python.org/pypi/inflect">inflect</a></li> <li><a href="https://pypi.python.org/pypi/inflect">inflect</a></li>
<li><a href="https://pypi.python.org/pypi/six">six</a></li>
</ul> </ul>
<h3>contributing</h3>
<h3 id="contributing">contributing</h3>
<p>please check out my <a href="https://github.com/modgethanc/ttbp/blob/master/.github/CONTRIBUTING.md">contributor <p>please check out my <a href="https://github.com/modgethanc/ttbp/blob/master/.github/CONTRIBUTING.md">contributor
guidelines</a> guidelines</a>
on github if you'd like to get involved with development!</p> on github if you'd like to get involved with development!</p>
<p>if you find any bugs or strange behavior, please message me locally on tildemail <p>if you find any bugs or strange behavior, please message me locally on tildemail
or open a github issue and i'll get back to you as soon as i can.</p> or open a github issue and i'll get back to you as soon as i can.</p>
<p>if you're interested in helping with the code, please drop me some tildemail!</p>
<p>if you're interested in helping with the code, please drop me some tildemail!</p> <p>i accept tips for development work on
<a href="https://liberapay.com/modgethanc">liberapay</a></p>
<h3>contributor shout-outs</h3>
<p>thanks to:</p>
<ul>
<li>~vilmibm, packaging help and gopher support</li>
<li>~sanqui, the bug swatter</li>
<li>~sinacutie, for css updates</li>
</ul>

196
README.md
View File

@ -11,14 +11,18 @@ this project.
`ttbp` runs from the command line, providing a hub for writing personal blog `ttbp` runs from the command line, providing a hub for writing personal blog
posts and reading posts written by other users of tilde.town. it's a little bit posts and reading posts written by other users of tilde.town. it's a little bit
like livejournal or dreamwidth or tumblr. you can opt to publish your posts to like livejournal or dreamwidth or tumblr. you can opt to publish your posts to a
a public html file hosted on your tilde page, or keep all your entries private public html file hosted on your tilde page, to tilde.town's gopher server, or
to the tilde.town server. keep all your entries private to the tilde.town server.
to use, run `feels` while logged in to tilde.town to use, run `feels` while logged in to tilde.town
(`feels` is a tilde.town specific command; if you're running this locally, or on this is a project that runs on tilde.town, so all users of this program are
a different server, run `ttbp` from the command line) expected to operate under the tilde.town [code of
conduct](http://tilde.town/wiki/conduct.html). content/personal issues should be
worked out according to the CoC, with support from the [administrative
team](http://tilde.town/wiki/administration/index.html) if needed.
### QUICK START ### QUICK START
@ -32,84 +36,184 @@ reading entries all happen within the program.
that's it! that's it!
### SUPPORT ### support
if you're having trouble getting started, or run into program errors or strange if you're having trouble getting started, or run into program errors or strange
behavior, please send internal tilde.town mail to ~endorphant and i'll try to behavior, please send internal tilde.town mail to ~endorphant and i'll try to
sort things out! sort things out!
there's also a function from the main menu that lets you send feedback/inquiries
to me directly; this uses internal tilde.town mail, which is what i'll respond
to.
### writing entries ### writing entries
entries are recorded as plaintext files in your ~/.ttbp/entries entries are recorded as plaintext files in your `~/.ttbp/entries` directory.
directory. you can edit them there directly, or fix old entries, or `ttbp` will use your selected editor to open and write files; each day is its
delete entries. own entry, like a diary page. at midnight for whatever timezone you've set for
your user account on tilde.town, you'll get a fresh entry. if you don't write
any feels on a particular day, no entries will show up there.
*warning*: changing old entries might cause strange things to when you save and quit the text editor, your entry will automatically propagate
happen with timestamps. the main program looks at the filename to the global feels list; if you've opted to publish your feels to html/gopher,
first for setting the date, then the last modified time to sort those files will update immediately. you can always go back to the current day's
recent posts. it expects YYYMMDD.txt as the filename; anything else entry and edit/add as you'd like, but older entries will not be available for
won't show up as a valid entry. yes, this means you can post things out editing from `ttbp`.
of date order by creating files with any date you want.
*(since files are just stored as plaintext in your directory, it's possible to
edit and move old entries directly from the command line. however, changing old
entries might cause strange things to happen with timestamps. the main program
looks at the filename first for setting the date, then the last modified time to
sort recent posts. it expects YYYMMDD.txt as the filename; anything else won't
show up as a valid entry. yes, this means you can post things out of date order
by creating files with any date you want.)*
#### general entry-writing notes #### general entry-writing notes
* you can use [markdown](https://daringfireball.net/projects/markdown/syntax) * you can use [markdown](https://daringfireball.net/projects/markdown/syntax)
* you can use html * you can use html
* you can also put things between `<!-- comments -->` to have them show up * you can also put things between `<!-- comments -->` to have them show up
in the feed but not render in a browser (but people can still read in the feed but not render in a browser (but people can still read
them with view-source) them with view-source)
### reading other feels
the `browse global feels` feature shows the ten most recent entries that anyone
has written on ttbp. this list is only accessible from within tilde.town,
although individual entries may be posted to html or gopher.
you can also pull up a list of a single user's feels through `check out your
neighbors`, which displays all users who are writing on `ttbp` based on their
most recently updated entry, and a link to their public html blog if they've
opted to publish their posts.
**please note!** entries written on `ttbp` should be considered sensitive,
private information, even if a particular user is publishing entries in a
world-viewable way! please be respectful about having access to other people's
feels, and do not copy/repeat any information without getting their explicit
permission. tilde.town operates on a high level of mutual trust, and `ttbp` is
designed to give individuals control over their content.
### privacy ### privacy
when you start your ttbp, you have the option of publishing or not publishing when you start your ttbp, you have the option of publishing or not publishing
your blog. your blog.
if you opt to publish, the program creates a directory `~/.ttbp/www` if you opt to not publish, your entires will never be accessible from outside of
where it stores all html files it generates, and symlinks this from your the tilde.town network; other tilde.town users will still be able to read your
`~/public_html` with your chosen blog directory. your blog will also be listed entries through the ttbp interface, or by directly accessing your
on the [main ttbp page](https://tilde.town/~endorphant/ttbp).
if you opt to not publish, your entires will never be accessible from outside
of the tilde.town network; other tilde.town users will still be able to read
your entries through the ttbp interface, or by directly accessing your
`~/.ttbp/entries` directory. `~/.ttbp/entries` directory.
if you want to further protect your entries, you can `chmod 700` your entries if you want to further protect your entries, you can `chmod 700` your entries
directory. directory.
if you opt to publish, the program creates a directory `~/.ttbp/www` where it
stores all html files it generates, and symlinks this from your `~/public_html`
with your chosen blog directory. your blog will also be listed on the [main ttbp
page](https://tilde.town/~endorphant/ttbp).
you can also opt to publish to gopher, and the program will automatically
generate a gophermap of your feels.
you can set publishing status on individual entries, or bury individual feels;
see "data management" below for details.
### data management
the `manage your feels` menu provides several tools for organizing your feels.
these are all actions you can perform manually from the command line, but doing
them from within the program can help keep your files properly linked up.
* **read over feels**--a list of all your entries, which you can open and
read like any other feel
* **modify feels publishing**--this lets you toggle privacy on individual
posts. entries marked `(nopub)` will not get written to html or gopher,
and toggling them from this menu will immediately publish or unpublish
that entry (if you're not publishing your posts at all, these settings
won't matter, since your feels will never show up outside of tilde.town)
* **backup your feels**--makes a .tar.gz of all your entries, saving one
copy to `~/.ttbp/backups/` with the current date, and a second copy to
your home directory for safekeeping.
* **import a feels backup**--unpacks a backup file into your current feels
list. this tool checks the `~/.ttbp/backups` directory for archives, and
expects a file created by the above backup utility. if it detects any file
collisions, it will preserve your current live copy and leave the backup
verison in a temp directory, and notify you that this happened. also, any
entries that were previously marked as `(nopub)` will retain their nopub
status.
* **bury some feels**--hides individual feels from viewing; entries are
moved to `~/.ttbp/buried` (and marked with a unique timestamp to prevent
file collision) with permissions set to 600, meaning no one except you
will be able to open that file. these entries are also hidden from your
own view from `read over feels`, and you'll have to open the files from
the command line if you want to see them. this is intended to be a
permament action, so you'll be asked to type the entry date once to load
the feel, then shown a preview of that feel, and then type the date again
to confirm burying.
* **delete feels by day**--*permanently removes individual entries*,
including deleting published html/gopher files if needed. this action is
not recoverable, unless you have a backup to restore; you'll be asked to
type the entry date once to load the feel, then shown a preview of that
feel, and then type the date again to confirm deletion.
* **purge all feels**--*permanently removes all feels*, including deleting
all published html/gopher files if needed. this action is not recoverable,
unless you have a backup to restore. you'll be asked to type a
one-time-use purge code to confirm this action.
* **wipe feels account**--*permanently removes all data associated with
feels*, including deleting any published hmtl/gopher files and removing
your `~/.ttbp` directory. any backups that you have in `~/.ttbp/backups`
will also be deleted with this action (which is why the backup function
makes a second copy for safekeeping in your home directory). you will no
longer show up in any lists as a user.
### settings
the settings menu lets you change specific options for handling your feels and
using the interface.
* **editor**--set your text editor
* **gopher**--opt in or out of automatically posting to gopher
* **post as nopub**--set whether posts default to being published or not
published (if you're not publishing your feels, this doesn't matter)
* **publish dir**--set the directory under you `public_html` where feels will be
published (if you're not publishing your feels, this defaults to `None`)
* **publishing**--opt in or out of automatically publishing entries to a
world-readable html page
* **rainbows**--opt in or out of having multicolored menu text
### changing your page layout ### changing your page layout
you can modify how your blog looks by editing the stylesheet or you can modify how your blog looks by editing the stylesheet or header and
header and footer files. the program sets you up with basic footer files. the program sets you up with basic default. if you break your page
default. if you break your page somehow, you can force the program to somehow, you can force the program to regenerate your configuration by deleting
regenerate your configuration by deleting your ~/.ttbp directory entirely. your ~/.ttbp directory entirely. **you might want to back up your
**you might want to back up your ~/.ttbp/entries directory before you do ~/.ttbp/entries directory before you do this.**
this.**
* to modify your stylesheet, edit your ~/.ttbp/config/style.css * to modify your stylesheet, edit your ~/.ttbp/config/style.css
* (future feature: having multiple stylesheets you can select)
* to modify the page header, edit your ~/.ttbp/config/header.txt * to modify the page header, edit your ~/.ttbp/config/header.txt
* you might note that there's a place marked off in the default header where * there's a place marked off in the default header where you can safely put
you can safely put custom HTML elements! custom HTML elements!
* to modify the page footer, edit your ~/.ttbp/config/footer.txt * to modify the page footer, edit your ~/.ttbp/config/footer.txt
### general tips/troubleshooting ### general tips/troubleshooting
* add `alias ttbp="~endorphant/bin/ttbp"` to your .bash_aliases for fewer keystrokes
* (similarly, `alias ttbp-beta="~endorphant/bin/ttbp-beta"`)
* if the date looks like it's ahead or behind, it's because you haven't set * if the date looks like it's ahead or behind, it's because you haven't set
your local timezone yet. here are some your local timezone yet. here are some
[timezone setting instructions](http://www.cyberciti.biz/faq/linux-unix-set-tz-environment-variable/) [timezone setting instructions](http://www.cyberciti.biz/faq/linux-unix-set-tz-environment-variable/)
* the feels burying tool will effectively clear your post for the day; you can
use this feature to start a fresh entry on a particular day by burying the
current day's feels and then editing a new file
### future features ### future features
these are a few ideas being kicked around, or under active development: these are a few ideas being kicked around, or under active development:
* better entry privacy/publish control options
* stylesheet/theme selector * stylesheet/theme selector
* responding to entries * better entry display within ttbp (currently just offloads to `less`)
* paginated list view * buried feels browser
* better entry display within ttbp
other ideas are listed on github as
[upcoming features](https://github.com/modgethanc/ttbp/issues?q=is%3Aissue+is%3Aopen+label%3A"upcoming+features") or [feature requests](https://github.com/modgethanc/ttbp/issues?q=is%3Aissue+is%3Aopen+label%3A"feature+request")!
### dependencies ### dependencies
@ -118,6 +222,7 @@ instance of this yourself)
* [mistune](https://pypi.python.org/pypi/mistune) * [mistune](https://pypi.python.org/pypi/mistune)
* [inflect](https://pypi.python.org/pypi/inflect) * [inflect](https://pypi.python.org/pypi/inflect)
* [six](https://pypi.python.org/pypi/six)
### contributing ### contributing
@ -129,3 +234,14 @@ if you find any bugs or strange behavior, please message me locally on tildemail
or open a github issue and i'll get back to you as soon as i can. or open a github issue and i'll get back to you as soon as i can.
if you're interested in helping with the code, please drop me some tildemail! if you're interested in helping with the code, please drop me some tildemail!
i accept tips for development work on
[liberapay](https://liberapay.com/modgethanc)
### contributor shout-outs
thanks to:
* ~vilmibm, packaging help and gopher support
* ~sanqui, the bug swatter
* ~sinacutie, for css updates

170
doc/manual.html 100644
View File

@ -0,0 +1,170 @@
<h1>FEELS MANUAL</h1>
<p><code>ttbp</code> stands for "tilde.town blogging platform", the original working name for
this project. the complete codebase is available on
<a href="https://github.com/modgethanc/ttbp">github</a>.</p>
<p><code>ttbp</code> runs from the command line, providing a hub for writing personal blog
posts and reading posts written by other users of tilde.town. it's a little bit
like livejournal or dreamwidth or tumblr. you can opt to publish your posts to a
public html file hosted on your tilde page, to tilde.town's gopher server, or
keep all your entries private to the tilde.town server.</p>
<p>this is a project that runs on tilde.town, so all users of this program are
expected to operate under the tilde.town <a href="http://tilde.town/wiki/conduct.html">code of
conduct</a>. content/personal issues should be
worked out according to the CoC, with support from the <a href="http://tilde.town/wiki/administration/index.html">administrative
team</a> if needed.</p>
<h3>support</h3>
<p>if you're having trouble getting started, or run into program errors or strange
behavior, please send internal tilde.town mail to ~endorphant and i'll try to
sort things out!</p>
<p>there's also a function from the main menu that lets you send feedback/inquiries
to me directly; this uses internal tilde.town mail, which is what i'll respond
to.</p>
<h3>writing entries</h3>
<p>entries are recorded as plaintext files in your <code>~/.ttbp/entries</code> directory.
<code>ttbp</code> will use your selected editor to open and write files; each day is its
own entry, like a diary page. at midnight for whatever timezone you've set for
your user account on tilde.town, you'll get a fresh entry. if you don't write
any feels on a particular day, no entries will show up there.</p>
<p>when you save and quit the text editor, your entry will automatically propagate
to the global feels list; if you've opted to publish your feels to html/gopher,
those files will update immediately. you can always go back to the current day's
entry and edit/add as you'd like, but older entries will not be available for
editing from <code>ttbp</code>.</p>
<p><em>(since files are just stored as plaintext in your directory, it's possible to
edit and move old entries directly from the command line. however, changing old
entries might cause strange things to happen with timestamps. the main program
looks at the filename first for setting the date, then the last modified time to
sort recent posts. it expects YYYMMDD.txt as the filename; anything else won't
show up as a valid entry. yes, this means you can post things out of date order
by creating files with any date you want.)</em></p>
<h4>general entry-writing notes</h4>
<ul>
<li>you can use <a href="https://daringfireball.net/projects/markdown/syntax">markdown</a></li>
<li>you can use html</li>
<li>you can also put things between <code>&lt;!-- comments --&gt;</code> to have them show up
in the feed but not render in a browser (but people can still read them with
view-source)</li>
</ul>
<h3>reading other feels</h3>
<p>the <code>browse global feels</code> feature shows the ten most recent entries that anyone
has written on ttbp. this list is only accessible from within tilde.town,
although individual entries may be posted to html or gopher.</p>
<p>you can also pull up a list of a single user's feels through <code>check out your
neighbors</code>, which displays all users who are writing on <code>ttbp</code> based on their
most recently updated entry, and a link to their public html blog if they've
opted to publish their posts.</p>
<p><strong>please note!</strong> entries written on <code>ttbp</code> should be considered sensitive,
private information, even if a particular user is publishing entries in a
world-viewable way! please be respectful about having access to other people's
feels, and do not copy/repeat any information without getting their explicit
permission. tilde.town operates on a high level of mutual trust, and <code>ttbp</code> is
designed to give individuals control over their content.</p>
<h3>privacy</h3>
<p>when you start your ttbp, you have the option of publishing or not publishing
your blog.</p>
<p>if you opt to not publish, your entires will never be accessible from outside of
the tilde.town network; other tilde.town users will still be able to read your
entries through the ttbp interface, or by directly accessing your
<code>~/.ttbp/entries</code> directory.</p>
<p>if you want to further protect your entries, you can <code>chmod 700</code> your entries
directory.</p>
<p>if you opt to publish, the program creates a directory <code>~/.ttbp/www</code> where it
stores all html files it generates, and symlinks this from your <code>~/public_html</code>
with your chosen blog directory. your blog will also be listed on the <a href="https://tilde.town/~endorphant/ttbp">main ttbp
page</a>.</p>
<p>you can also opt to publish to gopher, and the program will automatically
generate a gophermap of your feels.</p>
<p>you can set publishing status on individual entries, or bury individual feels;
see "data management" below for details.</p>
<h3>data management</h3>
<p>the <code>manage your feels</code> menu provides several tools for organizing your feels.
these are all actions you can perform manually from the command line, but doing
them from within the program can help keep your files properly linked up.</p>
<ul>
<li><strong>read over feels</strong>--a list of all your entries, which you can open and
read like any other feel</li>
<li><strong>modify feels publishing</strong>--this lets you toggle privacy on individual
posts. entries marked <code>(nopub)</code> will not get written to html or gopher,
and toggling them from this menu will immediately publish or unpublish
that entry (if you're not publishing your posts at all, these settings
won't matter, since your feels will never show up outside of tilde.town)</li>
<li><strong>backup your feels</strong>--makes a .tar.gz of all your entries, saving one
copy to <code>~/.ttbp/backups/</code> with the current date, and a second copy to
your home directory for safekeeping.</li>
<li><strong>import a feels backup</strong>--unpacks a backup file into your current feels
list. this tool checks the <code>~/.ttbp/backups</code> directory for archives, and
expects a file created by the above backup utility. if it detects any file
collisions, it will preserve your current live copy and leave the backup
verison in a temp directory, and notify you that this happened. also, any
entries that were previously marked as <code>(nopub)</code> will retain their nopub
status.</li>
<li><strong>bury some feels</strong>--hides individual feels from viewing; entries are
moved to <code>~/.ttbp/buried</code> (and marked with a unique timestamp to prevent
file collision) with permissions set to 600, meaning no one except you
will be able to open that file. these entries are also hidden from your
own view from <code>read over feels</code>, and you'll have to open the files from
the command line if you want to see them. this is intended to be a
permament action, so you'll be asked to type the entry date once to load
the feel, then shown a preview of that feel, and then type the date again
to confirm burying.</li>
<li><strong>delete feels by day</strong>--<em>permanently removes individual entries</em>,
including deleting published html/gopher files if needed. this action is
not recoverable, unless you have a backup to restore; you'll be asked to
type the entry date once to load the feel, then shown a preview of that
feel, and then type the date again to confirm deletion.</li>
<li><strong>purge all feels</strong>--<em>permanently removes all feels</em>, including deleting
all published html/gopher files if needed. this action is not recoverable,
unless you have a backup to restore. you'll be asked to type a
one-time-use purge code to confirm this action.</li>
<li><strong>wipe feels account</strong>--<em>permanently removes all data associated with
feels</em>, including deleting any published hmtl/gopher files and removing
your <code>~/.ttbp</code> directory. any backups that you have in <code>~/.ttbp/backups</code>
will also be deleted with this action (which is why the backup function
makes a second copy for safekeeping in your home directory). you will no
longer show up in any lists as a user.</li>
</ul>
<h3>settings</h3>
<p>the settings menu lets you change specific options for handling your feels and
using the interface.</p>
<ul>
<li><strong>editor</strong>--set your text editor</li>
<li><strong>gopher</strong>--opt in or out of automatically posting to gopher</li>
<li><strong>post as nopub</strong>--set whether posts default to being published or not
published (if you're not publishing your feels, this doesn't matter)</li>
<li><strong>publish dir</strong>--set the directory under you <code>public_html</code> where feels will be
published (if you're not publishing your feels, this defaults to <code>None</code>)</li>
<li><strong>publishing</strong>--opt in or out of automatically publishing entries to a
world-readable html page</li>
<li><strong>rainbows</strong>--opt in or out of having multicolored menu text</li>
</ul>
<h3>changing your page layout</h3>
<p>you can modify how your blog looks by editing the stylesheet or header and
footer files. the program sets you up with basic default. if you break your page
somehow, you can force the program to regenerate your configuration by deleting
your ~/.ttbp directory entirely. <strong>you might want to back up your
~/.ttbp/entries directory before you do this.</strong></p>
<ul>
<li>to modify your stylesheet, edit your ~/.ttbp/config/style.css</li>
<li>to modify the page header, edit your ~/.ttbp/config/header.txt</li>
<li>there's a place marked off in the default header where you can safely put
custom HTML elements!</li>
<li>to modify the page footer, edit your ~/.ttbp/config/footer.txt</li>
</ul>
<h3>general tips/troubleshooting</h3>
<ul>
<li>if the date looks like it's ahead or behind, it's because you haven't set
your local timezone yet. here are some
<a href="http://www.cyberciti.biz/faq/linux-unix-set-tz-environment-variable/">timezone setting instructions</a></li>
<li>the feels burying tool will effectively clear your post for the day; you can
use this feature to start a fresh entry on a particular day by burying the
current day's feels and then editing a new file</li>
</ul>
<h3>future features</h3>
<p>these are a few ideas being kicked around, or under active development:</p>
<ul>
<li>stylesheet/theme selector</li>
<li>better entry display within ttbp (currently just offloads to <code>less</code>)</li>
<li>buried feels browser</li>
</ul>
<p>other ideas are listed on github as
<a href="https://github.com/modgethanc/ttbp/issues?q=is%3Aissue+is%3Aopen+label%3A" title="upcoming+features">upcoming features</a> or <a href="https://github.com/modgethanc/ttbp/issues?q=is%3Aissue+is%3Aopen+label%3A" title="feature+request">feature requests</a>!</p>

196
doc/manual.md 100644
View File

@ -0,0 +1,196 @@
# FEELS MANUAL #
`ttbp` stands for "tilde.town blogging platform", the original working name for
this project. the complete codebase is available on
[github](https://github.com/modgethanc/ttbp).
`ttbp` runs from the command line, providing a hub for writing personal blog
posts and reading posts written by other users of tilde.town. it's a little bit
like livejournal or dreamwidth or tumblr. you can opt to publish your posts to a
public html file hosted on your tilde page, to tilde.town's gopher server, or
keep all your entries private to the tilde.town server.
this is a project that runs on tilde.town, so all users of this program are
expected to operate under the tilde.town [code of
conduct](http://tilde.town/wiki/conduct.html). content/personal issues should be
worked out according to the CoC, with support from the [administrative
team](http://tilde.town/wiki/administration/index.html) if needed.
### support
if you're having trouble getting started, or run into program errors or strange
behavior, please send internal tilde.town mail to ~endorphant and i'll try to
sort things out!
there's also a function from the main menu that lets you send feedback/inquiries
to me directly; this uses internal tilde.town mail, which is what i'll respond
to.
### writing entries
entries are recorded as plaintext files in your `~/.ttbp/entries` directory.
`ttbp` will use your selected editor to open and write files; each day is its
own entry, like a diary page. at midnight for whatever timezone you've set for
your user account on tilde.town, you'll get a fresh entry. if you don't write
any feels on a particular day, no entries will show up there.
when you save and quit the text editor, your entry will automatically propagate
to the global feels list; if you've opted to publish your feels to html/gopher,
those files will update immediately. you can always go back to the current day's
entry and edit/add as you'd like, but older entries will not be available for
editing from `ttbp`.
*(since files are just stored as plaintext in your directory, it's possible to
edit and move old entries directly from the command line. however, changing old
entries might cause strange things to happen with timestamps. the main program
looks at the filename first for setting the date, then the last modified time to
sort recent posts. it expects YYYMMDD.txt as the filename; anything else won't
show up as a valid entry. yes, this means you can post things out of date order
by creating files with any date you want.)*
#### general entry-writing notes
* you can use [markdown](https://daringfireball.net/projects/markdown/syntax)
* you can use html
* you can also put things between `<!-- comments -->` to have them show up
in the feed but not render in a browser (but people can still read them with
view-source)
### reading other feels
the `browse global feels` feature shows the ten most recent entries that anyone
has written on ttbp. this list is only accessible from within tilde.town,
although individual entries may be posted to html or gopher.
you can also pull up a list of a single user's feels through `check out your
neighbors`, which displays all users who are writing on `ttbp` based on their
most recently updated entry, and a link to their public html blog if they've
opted to publish their posts.
**please note!** entries written on `ttbp` should be considered sensitive,
private information, even if a particular user is publishing entries in a
world-viewable way! please be respectful about having access to other people's
feels, and do not copy/repeat any information without getting their explicit
permission. tilde.town operates on a high level of mutual trust, and `ttbp` is
designed to give individuals control over their content.
### privacy
when you start your ttbp, you have the option of publishing or not publishing
your blog.
if you opt to not publish, your entires will never be accessible from outside of
the tilde.town network; other tilde.town users will still be able to read your
entries through the ttbp interface, or by directly accessing your
`~/.ttbp/entries` directory.
if you want to further protect your entries, you can `chmod 700` your entries
directory.
if you opt to publish, the program creates a directory `~/.ttbp/www` where it
stores all html files it generates, and symlinks this from your `~/public_html`
with your chosen blog directory. your blog will also be listed on the [main ttbp
page](https://tilde.town/~endorphant/ttbp).
you can also opt to publish to gopher, and the program will automatically
generate a gophermap of your feels.
you can set publishing status on individual entries, or bury individual feels;
see "data management" below for details.
### data management
the `manage your feels` menu provides several tools for organizing your feels.
these are all actions you can perform manually from the command line, but doing
them from within the program can help keep your files properly linked up.
* **read over feels**--a list of all your entries, which you can open and
read like any other feel
* **modify feels publishing**--this lets you toggle privacy on individual
posts. entries marked `(nopub)` will not get written to html or gopher,
and toggling them from this menu will immediately publish or unpublish
that entry (if you're not publishing your posts at all, these settings
won't matter, since your feels will never show up outside of tilde.town)
* **backup your feels**--makes a .tar.gz of all your entries, saving one
copy to `~/.ttbp/backups/` with the current date, and a second copy to
your home directory for safekeeping.
* **import a feels backup**--unpacks a backup file into your current feels
list. this tool checks the `~/.ttbp/backups` directory for archives, and
expects a file created by the above backup utility. if it detects any file
collisions, it will preserve your current live copy and leave the backup
verison in a temp directory, and notify you that this happened. also, any
entries that were previously marked as `(nopub)` will retain their nopub
status.
* **bury some feels**--hides individual feels from viewing; entries are
moved to `~/.ttbp/buried` (and marked with a unique timestamp to prevent
file collision) with permissions set to 600, meaning no one except you
will be able to open that file. these entries are also hidden from your
own view from `read over feels`, and you'll have to open the files from
the command line if you want to see them. this is intended to be a
permament action, so you'll be asked to type the entry date once to load
the feel, then shown a preview of that feel, and then type the date again
to confirm burying.
* **delete feels by day**--*permanently removes individual entries*,
including deleting published html/gopher files if needed. this action is
not recoverable, unless you have a backup to restore; you'll be asked to
type the entry date once to load the feel, then shown a preview of that
feel, and then type the date again to confirm deletion.
* **purge all feels**--*permanently removes all feels*, including deleting
all published html/gopher files if needed. this action is not recoverable,
unless you have a backup to restore. you'll be asked to type a
one-time-use purge code to confirm this action.
* **wipe feels account**--*permanently removes all data associated with
feels*, including deleting any published hmtl/gopher files and removing
your `~/.ttbp` directory. any backups that you have in `~/.ttbp/backups`
will also be deleted with this action (which is why the backup function
makes a second copy for safekeeping in your home directory). you will no
longer show up in any lists as a user.
### settings
the settings menu lets you change specific options for handling your feels and
using the interface.
* **editor**--set your text editor
* **gopher**--opt in or out of automatically posting to gopher
* **post as nopub**--set whether posts default to being published or not
published (if you're not publishing your feels, this doesn't matter)
* **publish dir**--set the directory under you `public_html` where feels will be
published (if you're not publishing your feels, this defaults to `None`)
* **publishing**--opt in or out of automatically publishing entries to a
world-readable html page
* **rainbows**--opt in or out of having multicolored menu text
### changing your page layout
you can modify how your blog looks by editing the stylesheet or header and
footer files. the program sets you up with basic default. if you break your page
somehow, you can force the program to regenerate your configuration by deleting
your ~/.ttbp directory entirely. **you might want to back up your
~/.ttbp/entries directory before you do this.**
* to modify your stylesheet, edit your ~/.ttbp/config/style.css
* to modify the page header, edit your ~/.ttbp/config/header.txt
* there's a place marked off in the default header where you can safely put
custom HTML elements!
* to modify the page footer, edit your ~/.ttbp/config/footer.txt
### general tips/troubleshooting
* if the date looks like it's ahead or behind, it's because you haven't set
your local timezone yet. here are some
[timezone setting instructions](http://www.cyberciti.biz/faq/linux-unix-set-tz-environment-variable/)
* the feels burying tool will effectively clear your post for the day; you can
use this feature to start a fresh entry on a particular day by burying the
current day's feels and then editing a new file
### future features
these are a few ideas being kicked around, or under active development:
* stylesheet/theme selector
* better entry display within ttbp (currently just offloads to `less`)
* buried feels browser
other ideas are listed on github as
[upcoming features](https://github.com/modgethanc/ttbp/issues?q=is%3Aissue+is%3Aopen+label%3A"upcoming+features") or [feature requests](https://github.com/modgethanc/ttbp/issues?q=is%3Aissue+is%3Aopen+label%3A"feature+request")!

View File

@ -4,7 +4,7 @@ from setuptools import setup
setup( setup(
name='ttbp', name='ttbp',
version='0.11.2', version='0.12.0',
description='command line social blogging tool used on tilde.town', description='command line social blogging tool used on tilde.town',
url='https://github.com/modgethanc/ttbp', url='https://github.com/modgethanc/ttbp',
author='~endorphant', author='~endorphant',

View File

@ -1,6 +1,7 @@
from __future__ import absolute_import from __future__ import absolute_import
import os import os
import sys import sys
import time
from .. import util from .. import util
@ -67,11 +68,14 @@ USER_HOME = os.path.expanduser('~')
PATH = os.path.join(USER_HOME, '.ttbp') PATH = os.path.join(USER_HOME, '.ttbp')
PUBLIC = os.path.join(USER_HOME, 'public_html') PUBLIC = os.path.join(USER_HOME, 'public_html')
WWW = os.path.join(PATH, 'www') WWW = os.path.join(PATH, 'www')
GOPHER_ENTRIES = os.path.join(PATH, 'gopher')
GOPHER_PATH = os.path.join(USER_HOME, 'public_gopher', 'feels') GOPHER_PATH = os.path.join(USER_HOME, 'public_gopher', 'feels')
USER_CONFIG = os.path.join(PATH, 'config') USER_CONFIG = os.path.join(PATH, 'config')
TTBPRC = os.path.join(USER_CONFIG, 'ttbprc') TTBPRC = os.path.join(USER_CONFIG, 'ttbprc')
USER_DATA = os.path.join(PATH, 'entries') MAIN_FEELS = os.path.join(PATH, 'entries')
NOPUB = os.path.join(USER_CONFIG, "nopub") BURIED_FEELS = os.path.join(PATH, 'buried')
NOPUB = os.path.join(USER_CONFIG, 'nopub')
BACKUPS = os.path.join(PATH, 'backups')
## UI ## UI
@ -82,9 +86,102 @@ ___________________________________________________________
| ____ ____ ____ _ ____ ____ _ _ ____ _ _ _ ____ | | ____ ____ ____ _ ____ ____ _ _ ____ _ _ _ ____ |
| |___ |___ |___ | [__ |___ |\ | | __ | |\ | |___ | | |___ |___ |___ | [__ |___ |\ | | __ | |\ | |___ |
| | |___ |___ |___ ___] |___ | \| |__] | | \| |___ | | | |___ |___ |___ ___] |___ | \| |__] | | \| |___ |
| ver 0.11.2 (rainbows) | | <gan jue; to feel> ver 0.12.0 |
|__________________________________________________________| |__________________________________________________________|
'''.lstrip() '''.lstrip()
# ~ u n s t a b l e e x p e r i m e n t a l b r a n c h ~
#'''.lstrip()
## page texts
intro_prompt = """
i don't recognize you, stranger. let's make friends.
the feels engine is an internal blogging platform on tilde.town. it assists you
in recording your feels, giving you the option to publish to html or gopher, and
read the feels of other users on tilde.town.
press <enter> to set up an account, or <ctrl-c> to quit.
""".lstrip()
credits = """
ttbp was written for tilde.town by ~endorphant in python. the codebase is
publicly available on github at https://github.com/modgethanc/ttbp
tips for development are accepted at https://liberapay.com/modgethanc/
other contributors:
~vilmibm, packaging help and gopher support
~sanqui, the bug swatter
~sinacutie, for css updates
if you have ideas for ttbp, you are welcome to contact me to discuss them;
please send me tildemail or open a github issue. i am not a very experienced
developer, and ttbp is one of my first public-facing projects, so i appreciate
your patience while i learn how to be a better developer!
i'd love to hear about your ideas and brainstorm about new features!
thanks to everyone who reads, listens, writes, and feels."""
recording = """
feels will be recorded for today, {today}.
if you've already started recording feels for this day, you
can pick up where you left off.
you can write your feels in plaintext, markdown, html, or a mixture of
these.
press <enter> to begin recording your feels in your chosen text
editor.
""".format(today=time.strftime("%d %B %Y"))
bury_feels_prompt = """\
burying a feel removes it from view, including your own. buried feels are
stashed in a private directory at:
{buried_dir}
you can visit your feels there from the command line, but no one else can view
those files.
(a buried feels browser is in the works; for now, you'll have to use the
command line to view your buried feels)
which day's feels do you want to bury?
YYYYMMDD (or 'q' to cancel)> """.format(buried_dir=BURIED_FEELS)
account_wipe_prompt = """\
warning! ! ! this action is irreversible!!!
this tool will remove your entire presence from the feels engine. this includes
all posts, settings, and published html/gopher feels. you will no longer be
listed anywhere as a user here.
there is no way for me to help you recover any part of your feels acccount. i
respect your need to do this from time to time, so please be sure you're ready!
i recommend that you make a backup of your feels and stash them somewhere safe,
just in case a future version of you still wants to look them over."""
feels_purge_prompt = """\
warning! ! ! this action is irreversible!!!
there is no way for me to help you recover your feels if you purge them all. i
respect your need to do this from time to time, so please be sure you're ready!
i recommend that you make a backup of your feels and stash them somewhere safe,
just in case a future version of you still wants to look them over.
"""
mystery_error = """\
sorry, something went wrong! please try to address the error and try again.
if you need help, ask in IRC or send mail to ~endorphant and we'll try to
figure it out!
""".lstrip()
## update announcements ## update announcements
@ -152,6 +249,47 @@ version 0.9.3 features:
* added a new option to allow setting entries to default to either public or * added a new option to allow setting entries to default to either public or
non-public on posting; this option only really makes sense if you're non-public on posting; this option only really makes sense if you're
already publishing to html/gopher, but is available either way! already publishing to html/gopher, but is available either way!
you can find this option under 'settings' as 'post as nopub'.""" you can find this option under 'settings' as 'post as nopub'.""",
"0.11.3": """
~[version 0.11.3 update]~
* thanks to ~sinacutie, you can now set custom css for the permalink text
styling on your html page. the default permalink style has been added to
your current css file, and shouldn't change the appearance of your page.
if you're not using custom css, don't worry about this!""",
"0.12.0": """
~[version 0.12.0 update]~
a lot of new stuff this time! from the main menu, option (1) is now called
"manage your feels", which contains an expanded set of tools for organizing
your feels:
* read over feels (a list of all your entries)
* modify feels publishing (toggle privacy on individual entries)
* backup your feels (makes a .tar.gz of all your entries)
* import a feels backup (unpacks a backup into your feels)
* bury some feels (hide individual entries from view)
* delete feels by day (permanently remove individual entries)
* purge all feels (permanently remove all entries)
* wipe feels account (permanently remove everything associated with
ttbp)
each of these tools has expanded descriptions and instructions from the
menu, so check them out if you're curious! all of the irreversibl data
management actions have confirmation actions, so it's easy to cancel if you
accidentally access a tool.
also, i've updated the documentation file to reflect recent feature changes,
including some more details about how things work under the hood, and some
clarifications on how existing features work. please give it a read through
if you have a chance, and let me know if there's anything else i can
improve!
lastly, i just wanted to mention that i do accept tips for my dev work at
https://liberapay.com/modgethanc/ but there's no pressure to donate at all,
i'm just making this option available for anyone whose financially stable
and wants to kick some spare change my way; this is a labor of love, and i'm
happy to work on it regardless :)
"""
} }

View File

@ -28,6 +28,12 @@ body {
padding: 1em; padding: 1em;
} }
.entry p.permalink {
font-size: .6em;
font-color: #808080;
text-align: right;
}
.entry h5 { .entry h5 {
text-align: right; text-align: right;
margin-top: .2em; margin-top: .2em;

View File

@ -41,6 +41,8 @@ import json
from . import chatter from . import chatter
from . import config from . import config
from . import gopher
from . import util
FEED = os.path.join("/home", "endorphant", "public_html", "ttbp", "index.html") FEED = os.path.join("/home", "endorphant", "public_html", "ttbp", "index.html")
SETTINGS = {} SETTINGS = {}
@ -75,40 +77,42 @@ def reload_ttbprc(ttbprc={}):
SETTINGS = ttbprc SETTINGS = ttbprc
def get_files(feelsdir=config.MAIN_FEELS):
"""Returns a list of user's feels in the given directory (defaults to main
feels dir)"""
def get_files():
"""Returns a list of user's feels."""
files = [] files = []
for filename in os.listdir(config.USER_DATA): for filename in os.listdir(feelsdir):
if nopub(filename): if nopub(filename):
link = os.path.join(config.WWW, unpublish_feel(filename)
os.path.splitext( else:
os.path.basename(filename))[0]+".html") filename = os.path.join(feelsdir, filename)
if os.path.exists(link): if os.path.isfile(filename) and valid(filename):
subprocess.call(["rm", link]) files.append(filename)
continue
filename = os.path.join(config.USER_DATA, filename)
if os.path.isfile(filename) and valid(filename):
files.append(filename)
files.sort() files.sort()
files.reverse() files.reverse()
return files return files
def load_files(feelsdir=config.MAIN_FEELS):
def load_files():
''' '''
file loader file loader
* reads user's nopub file * reads user's nopub file
* loads all valid filenames that are not excluded in nopub to global files list * calls get_files() to load all files for given directory
* re-renders main html file and/or gopher if needed
''' '''
global FILES global FILES
load_nopubs() load_nopubs()
FILES = get_files() FILES = get_files(feelsdir)
if publishing():
write_html("index.html")
if SETTINGS.get('gopher'):
gopher.publish_gopher('feels', FILES)
def load_nopubs(): def load_nopubs():
"""Load a list of the user's nopub entries. """Load a list of the user's nopub entries.
@ -127,7 +131,7 @@ def load_nopubs():
## html outputting ## html outputting
def write(outurl="default.html"): def write_html(outurl="default.html"):
''' '''
main page renderer main page renderer
@ -167,7 +171,7 @@ def write_page(filename):
url url
''' '''
outurl = os.path.join(config.WWW, "".join(parse_date(filename))+".html") outurl = os.path.join(config.WWW, "".join(util.parse_date(filename))+".html")
outfile = open(outurl, "w") outfile = open(outurl, "w")
outfile.write("<!--generated by the tilde.town blogging platform on "+time.strftime("%d %B %y")+"\nhttp://tilde.town/~endorphant/ttbp/-->\n\n") outfile.write("<!--generated by the tilde.town blogging platform on "+time.strftime("%d %B %y")+"\nhttp://tilde.town/~endorphant/ttbp/-->\n\n")
@ -197,7 +201,7 @@ def write_entry(filename):
* return as list of strings * return as list of strings
''' '''
date = parse_date(filename) date = util.parse_date(filename)
entry = [ entry = [
"\t\t<p><a name=\""+date[0]+date[1]+date[2]+"\"></a><br /><br /></p>\n", "\t\t<p><a name=\""+date[0]+date[1]+date[2]+"\"></a><br /><br /></p>\n",
@ -207,7 +211,7 @@ def write_entry(filename):
] ]
raw = [] raw = []
rawfile = open(os.path.join(config.USER_DATA, filename), "r") rawfile = open(os.path.join(config.MAIN_FEELS, filename), "r")
for line in rawfile: for line in rawfile:
raw.append(line) raw.append(line)
@ -221,7 +225,7 @@ def write_entry(filename):
# entry.append("</p>\n\t\t\t<p>") # entry.append("</p>\n\t\t\t<p>")
#entry.append("</p>\n") #entry.append("</p>\n")
entry.append("\t\t\t<p style=\"font-size:.6em; font-color:#808080; text-align: right;\"><a href=\""+"".join(date)+".html\">permalink</a></p>\n") entry.append("\t\t\t<p class=\"permalink\"><a href=\""+"".join(date)+".html\">permalink</a></p>\n")
entry.append("\n\t\t</div>\n") entry.append("\n\t\t</div>\n")
return entry return entry
@ -316,7 +320,7 @@ def meta(entries = FILES):
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
wc = "???" wc = "???"
timestamp = time.strftime("%Y-%m-%d at %H:%M", time.localtime(mtime)) timestamp = time.strftime("%Y-%m-%d at %H:%M", time.localtime(mtime))
date = "-".join(parse_date(filename)) date = "-".join(util.parse_date(filename))
author = os.path.split(os.path.split(os.path.split(os.path.split(filename)[0])[0])[0])[1] author = os.path.split(os.path.split(os.path.split(os.path.split(filename)[0])[0])[0])[1]
meta.append([filename, mtime, wc, timestamp, date, author]) meta.append([filename, mtime, wc, timestamp, date, author])
@ -345,23 +349,6 @@ def valid(filename):
return True return True
def parse_date(file):
'''
parses date out of pre-validated filename
* assumes a filename of YYYYMMDD.txt
* returns a list:
[0] 'YYYY'
[1] 'MM'
[2] 'DD'
'''
rawdate = os.path.splitext(os.path.basename(file))[0]
date = [rawdate[0:4], rawdate[4:6], rawdate[6:]]
return date
def find_ttbps(): def find_ttbps():
''' '''
returns a list of users with a ttbp by checking for a valid ttbprc returns a list of users with a ttbp by checking for a valid ttbprc
@ -457,12 +444,7 @@ def toggle_nopub(filename):
NOPUBS.remove(filename) NOPUBS.remove(filename)
else: else:
NOPUBS.append(filename) NOPUBS.append(filename)
live_html = os.path.join(config.WWW, filename.split(".")[0]+".html") unpublish_feel(filename)
if os.path.exists(live_html):
subprocess.call(["rm", live_html])
live_gopher = os.path.join(config.GOPHER_PATH, filename)
if os.path.exists(live_gopher):
subprocess.call(["rm", live_gopher])
nopub_file = open(config.NOPUB, 'w') nopub_file = open(config.NOPUB, 'w')
nopub_file.write("""\ nopub_file.write("""\
@ -478,6 +460,93 @@ def toggle_nopub(filename):
return action return action
def bury_feel(filename):
"""buries given filename; this removes the feel from any publicly-readable
location, and moves the textfile to user's private feels directory.
timestring will be added to the filename to disambiguate and prevent
filename collisions.
creates buried feels dir if it doesn't exist.
regenerates feels list and republishes."""
if not os.path.exists(config.BURIED_FEELS):
os.mkdir(config.BURIED_FEELS)
subprocess.call(["chmod", "700", config.BURIED_FEELS])
buryname = os.path.splitext(os.path.basename(filename))[0]+"-"+str(int(time.time()))+".txt"
subprocess.call(["mv", os.path.join(config.MAIN_FEELS, filename), os.path.join(config.BURIED_FEELS, buryname)])
subprocess.call(["chmod", "600", os.path.join(config.BURIED_FEELS, buryname)])
if publishing():
unpublish_feel(filename)
load_files()
return os.path.join(config.BURIED_FEELS, buryname)
def delete_feel(filename):
"""deletes given filename; removes the feel from publicly-readable
locations, then deletes the original file."""
feel = os.path.join(config.MAIN_FEELS, filename)
if os.path.exists(feel):
subprocess.call(["rm", feel])
unpublish_feel(filename)
load_files(config.MAIN_FEELS)
def unpublish_feel(filename):
"""takes given filename and removes it from public_html and gopher_html, if
those locations exists. afterwards, regenerate index files appropriately."""
live_html = os.path.join(config.WWW,
os.path.splitext(os.path.basename(filename))[0]+".html")
if os.path.exists(live_html):
subprocess.call(["rm", live_html])
live_gopher = os.path.join(config.GOPHER_PATH, filename)
if os.path.exists(live_gopher):
subprocess.call(["rm", live_gopher])
def process_backup(filename):
"""takes given filename and unpacks it into a temp directory, then returns a
list of filenames with collisions filtered out.
ignores any invalidly named files or files that already exist, to avoid
clobbering current feels. ignored files are left in the archive directory
for the user to manually sort out."""
backup_dir = os.path.splitext(os.path.splitext(os.path.basename(filename))[0])[0]
backup_path = os.path.join(config.BACKUPS, backup_dir)
if not os.path.exists(backup_path):
subprocess.call(["mkdir", backup_path])
subprocess.call(["chmod", "700", backup_path])
subprocess.call(["tar", "-C", backup_path, "-xf", filename])
backup_entries = os.path.join(backup_path, "entries")
backups = os.listdir(backup_entries)
current = os.listdir(config.MAIN_FEELS)
imported = []
for feel in backups:
if os.path.basename(feel) not in current:
imported.append(os.path.join(backup_entries, feel))
imported.sort()
return imported
def import_feels(backups):
"""takes a list of filepaths and copies those to current main feels.
this does not check for collisions.
"""
pass
############# #############
############# #############

View File

@ -7,7 +7,8 @@ import time
import subprocess import subprocess
from . import util from . import util
from .core import parse_date from . import config
#from .core import parse_date
GOPHER_PROMPT = """ GOPHER_PROMPT = """
@ -73,7 +74,7 @@ def publish_gopher(gopher_path, entry_filenames):
if not os.path.exists(gopher_entry_symlink): if not os.path.exists(gopher_entry_symlink):
subprocess.call(["ln", "-s", entry_filename, gopher_entry_symlink]) subprocess.call(["ln", "-s", entry_filename, gopher_entry_symlink])
label = "-".join(parse_date(entry_filename)) label = "-".join(util.parse_date(entry_filename))
gophermap.write('0{file_label}\t{filename}\n'.format( gophermap.write('0{file_label}\t{filename}\n'.format(
file_label=label, file_label=label,
filename=filename)) filename=filename))
@ -107,3 +108,11 @@ def setup_gopher(gopher_path):
os.makedirs(gopher_entries) os.makedirs(gopher_entries)
subprocess.call(["ln", "-s", gopher_entries, ttbp_gopher]) subprocess.call(["ln", "-s", gopher_entries, ttbp_gopher])
def unpublish():
"""blanks all gopher things and recreates the directories."""
subprocess.call(["rm", "-rf", config.GOPHER_PATH])
subprocess.call(["rm", "-rf", config.GOPHER_ENTRIES])
os.mkdir(config.GOPHER_ENTRIES)
subprocess.call(["ln", "-s", config.GOPHER_ENTRIES, config.GOPHER_PATH])

View File

@ -34,6 +34,7 @@ https://github.com/modgethanc/ttbp
from __future__ import absolute_import from __future__ import absolute_import
import os import os
import sys
import tempfile import tempfile
import subprocess import subprocess
import time import time
@ -50,7 +51,7 @@ from . import chatter
from . import gopher from . import gopher
from . import util from . import util
__version__ = "0.11.2" __version__ = "0.12.0"
__author__ = "endorphant <endorphant@tilde.town)" __author__ = "endorphant <endorphant@tilde.town)"
p = inflect.engine() p = inflect.engine()
@ -73,6 +74,7 @@ DEFAULT_SETTINGS = {
"gopher": False, "gopher": False,
"publishing": False, "publishing": False,
"rainbows": False, "rainbows": False,
"post as nopub": False,
} }
## user globals ## user globals
@ -81,7 +83,8 @@ SETTINGS = {
"publish dir": None, "publish dir": None,
"gopher": False, "gopher": False,
"publishing": False, "publishing": False,
"rainbows": False "rainbows": False,
"post as nopub": False,
} }
## ttbp specific utilities ## ttbp specific utilities
@ -247,10 +250,7 @@ def init():
""" """
try: try:
input(""" input(config.intro_prompt)
i don't recognize you, stranger. let's make friends.
press <enter> to begin, or <ctrl-c> to get out of here.""")
except KeyboardInterrupt: except KeyboardInterrupt:
print("\n\nthanks for checking in! i'll always be here.\n\n") print("\n\nthanks for checking in! i'll always be here.\n\n")
quit() quit()
@ -259,7 +259,7 @@ press <enter> to begin, or <ctrl-c> to get out of here.""")
time.sleep(1) time.sleep(1)
print("...") print("...")
time.sleep(1) time.sleep(.5)
## record user in source list ## record user in source list
users = open(config.USERFILE, 'a') users = open(config.USERFILE, 'a')
@ -272,7 +272,7 @@ press <enter> to begin, or <ctrl-c> to get out of here.""")
print("\ngenerating feels at {path}...".format(path=config.PATH).rstrip()) print("\ngenerating feels at {path}...".format(path=config.PATH).rstrip())
subprocess.call(["mkdir", config.PATH]) subprocess.call(["mkdir", config.PATH])
subprocess.call(["mkdir", config.USER_CONFIG]) subprocess.call(["mkdir", config.USER_CONFIG])
subprocess.call(["mkdir", config.USER_DATA]) subprocess.call(["mkdir", config.MAIN_FEELS])
versionFile = os.path.join(config.PATH, "version") versionFile = os.path.join(config.PATH, "version")
open(versionFile, "w").write(__version__) open(versionFile, "w").write(__version__)
@ -291,13 +291,21 @@ press <enter> to begin, or <ctrl-c> to get out of here.""")
f.write(config.DEFAULT_STYLE) f.write(config.DEFAULT_STYLE)
## run user-interactive setup and load core engine ## run user-interactive setup and load core engine
time.sleep(1) time.sleep(0.5)
print("done setting up feels!") print("done setting up feels!")
print("\nthese are the default settings. you can change any of them now, or change them later at any time!!") print("\nthese are the default settings. you can change any of them now, or change them later at any time!!")
setup() setup()
core.load(SETTINGS) core.load(SETTINGS)
input("\nyou're all good to go, "+chatter.say("friend")+"! hit <enter> to continue.\n\n") input("""
you're all good to go, {friend}! if you have any questions about how things
work here, check out the documentation from the main menu, ask in IRC, or
drop ~endorphant a line!
hit <enter> to continue.
""".format(friend=chatter.say("friend")))
return "" return ""
def gen_header(): def gen_header():
@ -406,7 +414,7 @@ def setup_repair():
save_settings() save_settings()
print("...") print("...")
time.sleep(1) time.sleep(0.5)
input("\nyou're all good to go, "+chatter.say("friend")+"! hit <enter> to continue.\n\n") input("\nyou're all good to go, "+chatter.say("friend")+"! hit <enter> to continue.\n\n")
def setup(): def setup():
@ -436,68 +444,74 @@ def setup():
redraw(EJECT) redraw(EJECT)
return SETTINGS return SETTINGS
if choice in QUITS: if choice is not "":
if choice in QUITS:
redraw()
return SETTINGS
# editor selection
if settingList[int(choice)] == "editor":
SETTINGS.update({"editor": select_editor()})
redraw("text editor set to: {editor}".format(editor=SETTINGS["editor"]))
save_settings()
return setup()
# publishing selection
elif settingList[int(choice)] == "publishing":
SETTINGS.update({"publishing":select_publishing()})
core.reload_ttbprc(SETTINGS)
update_publishing()
redraw("publishing set to {publishing}".format(publishing=SETTINGS.get("publishing")))
save_settings()
return setup()
# publish dir selection
elif settingList[int(choice)] == "publish dir":
publish_dir = select_publish_dir()
SETTINGS.update({"publish dir": publish_dir})
#update_publishing()
if publish_dir is None:
redraw("sorry, i can't set a publish directory for you if you don't have html publishing enabled. please enable publishing to continue.")
else:
redraw("publishing your entries to {url}/index.html".format(
url="/".join([config.LIVE+config.USER,
str(SETTINGS.get("publish dir"))])))
save_settings()
return setup()
# gopher opt-in
elif settingList[int(choice)] == "gopher":
SETTINGS.update({'gopher': gopher.select_gopher()})
redraw('gopher publishing set to: {gopher}'.format(gopher=SETTINGS['gopher']))
update_gopher()
save_settings()
return setup()
# rainbow menu selection
elif settingList[int(choice)] == "rainbows":
SETTINGS.update({"rainbows": toggle_rainbows()})
redraw("rainbow menus set to {rainbow}".format(rainbow=SETTINGS.get("rainbows")))
save_settings()
return setup()
#nopub toggling
elif settingList[int(choice)] == "post as nopub":
SETTINGS.update({"post as nopub": toggle_pub_default()})
redraw("posting default set to {nopub}".format(nopub=SETTINGS.get("post as nopub")))
save_settings()
return setup()
input("\nyou're all good to go, {friend}! hit <enter> to continue.\n\n".format(friend=chatter.say("friend")))
redraw() redraw()
return SETTINGS return SETTINGS
# editor selection else:
if settingList[int(choice)] == "editor": redraw("now changing your settings. press <ctrl-c> if you didn't mean to do this.")
SETTINGS.update({"editor": select_editor()})
redraw("text editor set to: {editor}".format(editor=SETTINGS["editor"]))
save_settings()
return setup() return setup()
# publishing selection
elif settingList[int(choice)] == "publishing":
SETTINGS.update({"publishing":select_publishing()})
core.reload_ttbprc(SETTINGS)
update_publishing()
redraw("publishing set to {publishing}".format(publishing=SETTINGS.get("publishing")))
save_settings()
return setup()
# publish dir selection
elif settingList[int(choice)] == "publish dir":
publish_dir = select_publish_dir()
SETTINGS.update({"publish dir": publish_dir})
#update_publishing()
if publish_dir is None:
redraw("sorry, i can't set a publish directory for you if you don't have html publishing enabled. please enable publishing to continue.")
else:
redraw("publishing your entries to {url}/index.html".format(
url="/".join([config.LIVE+config.USER,
str(SETTINGS.get("publish dir"))])))
save_settings()
return setup()
# gopher opt-in
elif settingList[int(choice)] == "gopher":
SETTINGS.update({'gopher': gopher.select_gopher()})
redraw('gopher publishing set to: {gopher}'.format(gopher=SETTINGS['gopher']))
update_gopher()
save_settings()
return setup()
# rainbow menu selection
elif settingList[int(choice)] == "rainbows":
SETTINGS.update({"rainbows": toggle_rainbows()})
redraw("rainbow menus set to {rainbow}".format(rainbow=SETTINGS.get("rainbows")))
save_settings()
return setup()
#nopub toggling
elif settingList[int(choice)] == "post as nopub":
SETTINGS.update({"post as nopub": toggle_pub_default()})
redraw("posting default set to {nopub}".format(nopub=SETTINGS.get("post as nopub")))
save_settings()
return setup()
input("\nyou're all good to go, {friend}! hit <enter> to continue.\n\n".format(friend=chatter.say("friend")))
redraw()
return SETTINGS
def save_settings(): def save_settings():
""" """
Save current settings. Save current settings.
@ -516,7 +530,7 @@ def main_menu():
menuOptions = [ menuOptions = [
"record your feels", "record your feels",
"review your feels", "manage your feels",
"check out your neighbors", "check out your neighbors",
"browse global feels", "browse global feels",
"scribble some graffiti", "scribble some graffiti",
@ -537,18 +551,13 @@ def main_menu():
if choice == '0': if choice == '0':
redraw() redraw()
today = time.strftime("%Y%m%d") today = time.strftime("%Y%m%d")
write_entry(os.path.join(config.USER_DATA, today+".txt")) write_entry(os.path.join(config.MAIN_FEELS, today+".txt"))
core.www_neighbors() core.www_neighbors()
elif choice == '1': elif choice == '1':
if core.publishing(): intro = "here are some options for managing your feels:"
intro = "here are some options for reviewing your feels:" redraw(intro)
redraw(intro) review_menu(intro)
review_menu(intro) core.load_files()
core.load_files()
core.write("index.html")
else:
redraw("your recorded feels, listed by date:")
view_feels(config.USER)
elif choice == '2': elif choice == '2':
users = core.find_ttbps() users = core.find_ttbps()
prompt = "the following {usercount} {are} recording feels on ttbp:".format( prompt = "the following {usercount} {are} recording feels on ttbp:".format(
@ -571,7 +580,7 @@ def main_menu():
redraw() redraw()
show_credits() show_credits()
elif choice == '8': elif choice == '8':
subprocess.call(["lynx", os.path.join(config.INSTALL_PATH, "..", "README.html")]) subprocess.call(["lynx", os.path.join(config.INSTALL_PATH, "..", "doc", "manual.html")])
redraw() redraw()
elif choice in QUITS: elif choice in QUITS:
return stop() return stop()
@ -614,26 +623,72 @@ def review_menu(intro=""):
menuOptions = [ menuOptions = [
"read over feels", "read over feels",
"modify feels publishing" "modify feels publishing",
"backup your feels",
"import a feels backup",
"bury some feels",
"delete feels by day",
"purge all feels",
"wipe feels account"
] ]
util.print_menu(menuOptions, SETTINGS.get("rainbows", False)) util.print_menu(menuOptions, SETTINGS.get("rainbows", False))
choice = util.list_select(menuOptions, "what would you like to do with your feels? (or 'back' to return home) ") choice = util.list_select(menuOptions, "what would you like to do with your feels? (or 'q' to return home) ")
top = ""
hasfeels = len(os.listdir(config.MAIN_FEELS)) > 0
nofeels = "you don't have any feels to work with, "+chatter.say("friend")+"\n\n> "
if choice is not False: if choice is not False:
if choice == 0: if choice == 0:
redraw("your recorded feels, listed by date:") if hasfeels:
view_feels(config.USER) redraw("your recorded feels, listed by date:")
view_feels(config.USER)
else:
top = nofeels
elif choice == 1: elif choice == 1:
redraw("publishing status of your feels:") if hasfeels:
list_nopubs(config.USER) redraw("publishing status of your feels:")
list_nopubs(config.USER)
else:
top = nofeels
elif choice == 2:
if hasfeels:
redraw("FEELS BACKUP")
backup_feels()
else:
top = nofeels
elif choice == 3:
redraw("loading feels backup")
load_backup()
elif choice == 4:
if hasfeels:
redraw("burying feels")
bury_feels()
else:
top = nofeels
elif choice == 5:
if hasfeels:
redraw("deleting feels")
delete_feels()
else:
top = nofeels
elif choice == 6:
if hasfeels:
redraw("!!!PURGING ALL FEELS!!!")
purge_feels()
else:
top = nofeels
elif choice == 7:
redraw("!!! WIPING FEELS ACCOUNT !!!")
wipe_account()
else: else:
redraw() redraw()
return return
redraw(intro) redraw(top+intro)
return review_menu() return review_menu(intro)
def view_neighbors(users, prompt): def view_neighbors(users, prompt):
''' '''
@ -694,7 +749,7 @@ def view_neighbors(users, prompt):
sortedUsers.append(user[0]) sortedUsers.append(user[0])
userIndex.append(user[2]) userIndex.append(user[2])
choice = menu_handler(sortedUsers, "pick a townie to browse their feels, or type 'back' or 'q' to go home: ", 15, SETTINGS.get("rainbows", False), prompt) choice = menu_handler(sortedUsers, "pick a townie to browse their feels, or type 'q' to go home: ", 15, SETTINGS.get("rainbows", False), prompt)
if choice is not False: if choice is not False:
redraw("~{user}'s recorded feels, listed by date: \n".format(user=userIndex[choice])) redraw("~{user}'s recorded feels, listed by date: \n".format(user=userIndex[choice]))
@ -708,7 +763,6 @@ def view_feels(townie):
''' '''
generates a list of all feels by given townie and displays in generates a list of all feels by given townie and displays in
date order; allows selection of one feel to read. date order; allows selection of one feel to read.
''' '''
metas, owner = generate_feels_list(townie) metas, owner = generate_feels_list(townie)
@ -733,7 +787,7 @@ def generate_feels_list(user):
showpub = False showpub = False
if user == config.USER: if user == config.USER:
entryDir = config.USER_DATA entryDir = config.MAIN_FEELS
owner = "your" owner = "your"
if core.publishing(): if core.publishing():
showpub = True showpub = True
@ -749,29 +803,320 @@ def generate_feels_list(user):
return metas, owner return metas, owner
def backup_feels():
"""creates a tar.gz of user's entries directory"""
backupfile = os.path.join(os.path.expanduser('~'), "feels-backup-"+time.strftime("%Y%m%d-%H%M%S")+".tar.gz")
print("""\
i'm preparing all of your entries for backup.""")
print("...")
time.sleep(1)
print("""
ready to go! a backup file will be saved to your home directory at:
{backuploc}""".format(backuploc=backupfile))
ans = util.input_yn("""\
would you like to create this backup?
please enter""")
if ans:
if not subprocess.call(["tar", "-C", config.PATH, "-czf", backupfile, "entries"]):
subprocess.call(["chmod", "600", backupfile])
if not os.path.exists(config.BACKUPS):
subprocess.call(["mkdir", config.BACKUPS])
subprocess.call(["chmod", "700", config.BACKUPS])
subprocess.call(["cp", backupfile, config.BACKUPS])
print("\nbackup saved! i also put a copy at {backup_dir} for you.".format(backup_dir = config.BACKUPS))
else:
print(config.mystery_error)
else:
print("no problem, {friend}; come back whenever if you want a backup!".format(friend=chatter.say("friend")))
input("\n\npress <enter> to go back to managing your feels.\n\n")
redraw()
return
def delete_feels():
"""handles deleting feels one at a time"""
feel = input("""which day's feels do you want to load for deletion?
YYYYMMDD (or 'q' to cancel)> """)
if feel in util.BACKS:
return
print("...")
time.sleep(0.1)
print("""\
here's a preview of that feel. press <q> when you're done reviewing!
-------------------------------------------------------------""")
if subprocess.call(["less", os.path.join(config.MAIN_FEELS, feel+".txt")]):
redraw("deleting feels")
print("""\
sorry, i couldn't find feels for {date}!
please try again, or type <q> to cancel.
""".format(date=feel))
return delete_feels()
print("""
-------------------------------------------------------------
feels deletion is irreversible! if you're sure you want to delete this feel,
type the date again to confirm, or 'q' to cancel.""")
confirm = input("[{feeldate}]> ".format(feeldate=feel))
if confirm == feel:
print("...")
time.sleep(0.5)
core.delete_feel(feel+".txt")
print("feels deleted!")
else:
print("deletion canceled!")
ans = util.input_yn("""do you want to delete a different feel?
please enter""")
if ans:
redraw("deleting feels")
return delete_feels()
else:
print("okay! please come back any time if you want to delete old feels!")
input("\n\npress <enter> to go back to managing your feels.\n\n")
redraw()
return
def purge_feels():
"""handles deleting all feels"""
print(config.feels_purge_prompt)
print("...")
time.sleep(0.5)
print("...loading feels...")
time.sleep(1)
print("...")
feelscount = len(os.listdir(config.MAIN_FEELS))
if feelscount > 0:
purgecode = util.genID(5)
print("""
i've loaded up all {count} of your feels for purging. if you're ready, carefully
type the following purge code:
_________
| |
| {purgecode} |
|_______|
""".format(purgecode=purgecode, count=feelscount))
ans = input("(leave blank or type anything else to cancel) > ")
if ans == purgecode:
print("...")
time.sleep(0.5)
unpublish()
if not subprocess.call(["rm", "-rf", config.MAIN_FEELS]):
subprocess.call(["mkdir", config.MAIN_FEELS])
core.load_files()
print("ALL FEELS PURGED! you're ready to start fresh!")
else:
print(config.mystery_error)
else:
print("\nfeels purge canceled! you're welcome to come back again.")
else:
print("you don't have any feels to purge, "+chatter.say("friend"))
input("\n\npress <enter> to go back to managing your feels.\n\n")
redraw()
return
def wipe_account():
"""handles wiping feels account"""
print(config.account_wipe_prompt)
print("...")
time.sleep(0.5)
print("...packaging up all your feels...")
time.sleep(1)
print("...")
purgecode = util.genID(5)
print("""
your account is all packed up! if you're ready, carefully type the following
purge code:
_________
| |
| {purgecode} |
|_______|
""".format(purgecode=purgecode))
ans = input("(leave blank or type anything else to cancel) > ")
if ans == purgecode:
print("...")
time.sleep(0.5)
unpublish()
if core.publishing():
publishDir = os.path.join(config.PUBLIC, SETTINGS.get("publish dir"))
make_publish_dir(publishDir)
if not subprocess.call(["rm", "-rf", config.PATH]):
print("""
account deleted! if you ever want to come back, you're always welcome to start
fresh :)
thank you for sharing your feels!""")
input("\n\npress <enter> to exit the feels engine.\n\n")
sys.exit(stop())
else:
print(config.mystery_error)
else:
print("\naccount deletion canceled! you're welcome to come back again.")
input("\n\npress <enter> to go back to managing your feels.\n\n")
redraw()
return
def load_backup():
"""
scans for archive files (prompting for a file location if not found), opens,
copies files to main feels directory (skipping ones that already exist)
"""
print("scanning backup directory at {directory}...".format(directory=config.BACKUPS))
time.sleep(.5)
print("...\n")
backups = []
try:
for filename in os.listdir(config.BACKUPS):
if "feels-backup" in filename and ".tar" in filename:
backups.append(filename)
except FileNotFoundError:
subprocess.call(["mkdir", config.BACKUPS])
if len(backups) < 1:
print("""
sorry, i didn't find any feels backups! if you have a backup file handy, please
move it to {directory} and try running this tool again.\
""".format(directory=config.BACKUPS))
else:
print("backup files found:\n")
choice = menu_handler(backups, "pick a backup file to load (or 'q' to cancel): ", 15, SETTINGS.get("rainbows", False), "backup files found:")
if choice is not False:
imports = core.process_backup(os.path.join(config.BACKUPS, backups[choice]))
for feel in imports:
print("importing {entry}".format(entry="-".join(util.parse_date(feel))))
subprocess.call(["mv", feel, config.MAIN_FEELS])
time.sleep(.01)
core.load_files()
tempdir = os.path.join(config.BACKUPS, os.path.splitext(os.path.splitext(os.path.basename(backups[choice]))[0])[0], "entries")
time.sleep(.5)
print("...\n")
if len(os.listdir(tempdir)) == 0:
os.rmdir(tempdir)
print("congrats! your feels archive has been unloaded.")
else:
print("""\
i've unloaded as much as i can, but there are still some feels i didn't copy
over. this is probably because you have current feels on the same days, and i
didn't want to overwrite them.
you can check out the leftover feels yourself at:
{directory}""".format(directory=tempdir))
else:
return
input("\n\npress <enter> to go back to managing your feels.\n\n")
return
def bury_feels():
"""queries for a feel to bury, then calls the feels burying handler.
"""
feel = input(config.bury_feels_prompt)
if feel in util.BACKS:
return
print("...")
time.sleep(0.1)
print("""\
here's a preview of that feel. press <q> when you're done reviewing!
-------------------------------------------------------------""")
if subprocess.call(["less", os.path.join(config.MAIN_FEELS, feel+".txt")]):
redraw("burying feels")
print("""\
sorry, i couldn't find feels for {date}!
please try again, or type <q> to cancel.
""".format(date=feel))
return delete_feels()
print("""
-------------------------------------------------------------
feels burying is irreversible! if you're sure you want to bury this feel,
type the date again to confirm, or 'q' to cancel.
""")
confirm = input("[{feeldate}]> ".format(feeldate=feel))
if confirm == feel:
print("...")
time.sleep(0.5)
core.bury_feel(feel+".txt")
print("feels buried!")
else:
print("burying canceled!")
ans = util.input_yn("""do you want to bury a different feel? please enter""")
if ans:
redraw("burying feels")
return bury_feels()
else:
print("okay! please come back any time if you want to bury your feels!")
input("\n\npress <enter> to go back to managing your feels.\n\n")
redraw()
return
def show_credits(): def show_credits():
''' '''
prints author acknowledgements and commentary prints author acknowledgements and commentary
''' '''
print(""" print(config.credits)
ttbp was written for tilde.town by ~endorphant in python. the codebase is
publicly available on github at https://github.com/modgethanc/ttbp
other contributors:
~vilmibm, packaging help and gopher support
~sanqui, the bug swatter
if you have ideas for ttbp, you are welcome to contact me to discuss them;
please send me tildemail or open a github issue. i am not a very experienced
developer, and ttbp is one of my first public-facing projects, so i appreciate
your patience while i learn how to be a better developer!
i'd love to hear about your ideas and brainstorm about new features!
thanks to everyone who reads, listens, writes, and feels.\
""")
input("\n\npress <enter> to go back home.\n\n") input("\n\npress <enter> to go back home.\n\n")
redraw() redraw()
@ -779,24 +1124,12 @@ thanks to everyone who reads, listens, writes, and feels.\
## handlers ## handlers
def write_entry(entry=os.path.join(config.USER_DATA, "test.txt")): def write_entry(entry=os.path.join(config.MAIN_FEELS, "test.txt")):
''' '''
main feels-recording handler main feels-recording handler
''' '''
entered = input(""" entered = input(config.recording)
feels will be recorded for today, {today}.
if you've already started recording feels for this day, you
can pick up where you left off.
you can write your feels in plaintext, markdown, html, or a mixture of
these.
press <enter> to begin recording your feels in your chosen text
editor.
""".format(today=time.strftime("%d %B %Y")))
if entered: if entered:
entryFile = open(entry, "a") entryFile = open(entry, "a")
@ -806,22 +1139,24 @@ editor.
left = "" left = ""
core.load_files()
if SETTINGS.get("post as nopub"): if SETTINGS.get("post as nopub"):
core.toggle_nopub(os.path.basename(entry)) core.toggle_nopub(os.path.basename(entry))
else: else:
if core.publishing(): if core.publishing():
core.write("index.html") core.write_html("index.html")
left = "posted to {url}/index.html\n\n>".format( left = "posted to {url}/index.html\n\n> ".format(
url="/".join( url="/".join(
[config.LIVE+config.USER, [config.LIVE+config.USER,
str(SETTINGS.get("publish dir"))])) str(SETTINGS.get("publish dir"))]))
if SETTINGS.get('gopher'): if SETTINGS.get('gopher'):
gopher.publish_gopher('feels', core.get_files()) gopher.publish_gopher('feels', core.FILES)
left += " also posted to your ~/public_gopher!\n" left += "also posted to your ~/public_gopher!\n\n> "
core.load_files() #core.load_files()
redraw(left + " thanks for sharing your feels!") redraw(left + "thanks for sharing your feels!")
return return
@ -841,6 +1176,14 @@ def set_nopubs(metas, user, prompt):
"""displays a list of entries for pub/nopub toggling. """displays a list of entries for pub/nopub toggling.
""" """
if core.publishing():
nopub_note = ""
else:
nopub_note = """\
(since you're not publishing your entries, these settings don't really matter;
none of your feels will be viewable outside of this server)"""
print(nopub_note + "\n")
entries = [] entries = []
for entry in metas: for entry in metas:
pub = "" pub = ""
@ -848,14 +1191,13 @@ def set_nopubs(metas, user, prompt):
pub = "(nopub)" pub = "(nopub)"
entries.append(""+entry[4]+" ("+p.no("word", entry[2])+") "+"\t"+pub) entries.append(""+entry[4]+" ("+p.no("word", entry[2])+") "+"\t"+pub)
choice = menu_handler(entries, "pick an entry from the list, or type 'q' to go back: ", 10, SETTINGS.get("rainbows", False), prompt) choice = menu_handler(entries, "pick an entry from the list to toggle nopub status, or type 'q' to go back: ", 10, SETTINGS.get("rainbows", False), prompt+"\n\n"+nopub_note)
if choice is not False: if choice is not False:
target = os.path.basename(metas[choice][0]) target = os.path.basename(metas[choice][0])
action = core.toggle_nopub(target) action = core.toggle_nopub(target)
redraw(prompt) redraw(prompt)
core.write("index.html")
if SETTINGS["gopher"]: if SETTINGS["gopher"]:
gopher.publish_gopher('feels', core.get_files()) gopher.publish_gopher('feels', core.get_files())
@ -1107,14 +1449,14 @@ def select_publish_dir():
publishDir = os.path.join(config.PUBLIC, choice) publishDir = os.path.join(config.PUBLIC, choice)
while os.path.exists(publishDir): while os.path.exists(publishDir):
second = input("\n"+publishDir+"""\ second = input("""
already exists! {pDir} already exists!
setting this as your publishing directory means this program may setting this as your publishing directory means this program may
delete or overwrite file there! delete or overwrite file there!
if you're sure you want to use it, hit <enter> to confirm. if you're sure you want to use it, hit <enter> to confirm.
otherwise, pick another location: """) otherwise, pick another location: """.format(pDir=publishDir))
if second == "": if second == "":
break break
@ -1159,11 +1501,14 @@ def unpublish():
if directory: if directory:
publishDir = os.path.join(config.PUBLIC, directory) publishDir = os.path.join(config.PUBLIC, directory)
subprocess.call(["rm", publishDir]) if os.path.exists(publishDir):
subprocess.call(["rm", "-rf", publishDir])
subprocess.call(["rm", "-rf", config.WWW])
make_publish_dir(SETTINGS.get("publish dir"))
#SETTINGS.update({"publish dir": None})
if SETTINGS.get("gopher"): if SETTINGS.get("gopher"):
SETTINGS.update({"gopher": False}) gopher.unpublish()
subprocess.call(["rm", config.GOPHER_PATH])
def update_publishing(): def update_publishing():
''' '''
@ -1180,14 +1525,13 @@ def update_publishing():
subprocess.call(["rm", os.path.join(config.PUBLIC, oldDir)]) subprocess.call(["rm", os.path.join(config.PUBLIC, oldDir)])
make_publish_dir(newDir) make_publish_dir(newDir)
core.load_files() core.load_files()
core.write("index.html") #core.write_html("index.html")
else: else:
unpublish() unpublish()
SETTINGS.update({"publish dir": None})
core.load(SETTINGS) core.load(SETTINGS)
def make_publish_dir(dir): def make_publish_dir(publish_dir):
''' '''
setup helper to create publishing directory setup helper to create publishing directory
''' '''
@ -1200,13 +1544,18 @@ def make_publish_dir(dir):
index.write("<h1>ttbp blog placeholder</h1>") index.write("<h1>ttbp blog placeholder</h1>")
index.close() index.close()
publishDir = os.path.join(config.PUBLIC, dir) if core.publishing():
if os.path.exists(publishDir): live = os.path.join(config.PUBLIC, publish_dir)
subprocess.call(["rm", publishDir]) if os.path.exists(live):
subprocess.call(["rm", live])
subprocess.call(["ln", "-s", config.WWW, publishDir]) subprocess.call(["ln", "-s", config.WWW, live])
print("\n\tpublishing to "+config.LIVE+config.USER+"/"+SETTINGS.get("publish dir")+"/\n\n") return "\n\tpublishing to "+config.LIVE+config.USER+"/"+SETTINGS.get("publish dir")+"/\n\n"
else:
return ""
#print("\n\tpublishing to "+config.LIVE+config.USER+"/"+SETTINGS.get("publish dir")+"/\n\n")
def update_gopher(): def update_gopher():
''' '''
@ -1255,7 +1604,7 @@ def update_user_version():
time.sleep(1) time.sleep(1)
print("...") print("...")
time.sleep(1) time.sleep(0.5)
userVersion = "" userVersion = ""
(x, y, z) = [0, 0, 0] (x, y, z) = [0, 0, 0]
@ -1284,7 +1633,7 @@ def update_user_version():
# repopulate html files # repopulate html files
core.load_files() core.load_files()
core.write("index.html") #core.write_html("index.html")
# add publishing setting # add publishing setting
print("\nnew feature!\n") print("\nnew feature!\n")
@ -1335,6 +1684,19 @@ def update_user_version():
SETTINGS.update({"post as nopub": False}) SETTINGS.update({"post as nopub": False})
save_settings() save_settings()
if z < 3:
# update permalink css
style = open(os.path.join(config.USER_CONFIG, 'style.css'), 'r').read()
if "permalink" not in style:
print("adding new css...")
with open(os.path.join(config.USER_CONFIG, 'style.css'), 'a') as f:
f.write("""
.entry p.permalink {
font-size: .6em;
font-color: #808080;
text-align: right;
}""")
print(""" print("""
you're all good to go, """+chatter.say("friend")+"""! please contact ~endorphant if you're all good to go, """+chatter.say("friend")+"""! please contact ~endorphant if
something strange happened to you during this update. something strange happened to you during this update.
@ -1356,6 +1718,14 @@ something strange happened to you during this update.
# version 0.11.2 patch notes # version 0.11.2 patch notes
print(config.UPDATES["0.11.2"]) print(config.UPDATES["0.11.2"])
if y < 11 or z < 3:
# version 0.11.3 patch notes
print(config.UPDATES["0.11.3"])
if y < 12:
# version 0.12.0 patch notes
print(config.UPDATES["0.12.0"])
confirm = "" confirm = ""
while confirm not in ("x", "<x>", "X", "<X>"): while confirm not in ("x", "<x>", "X", "<X>"):

View File

@ -26,12 +26,13 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import random import random
import time import time
from six.moves import input from six.moves import input
import os
import colorama import colorama
import inflect import inflect
## misc globals ## misc globals
BACKS = ['back', 'b', 'q'] BACKS = ['back', 'b', 'q', '<q>']
NAVS = ['u', 'd'] NAVS = ['u', 'd']
## color stuff ## color stuff
@ -214,3 +215,21 @@ def input_yn(query):
ans = input("'y' or 'n' please: ") ans = input("'y' or 'n' please: ")
return ans == "y" return ans == "y"
def parse_date(file):
'''
parses date out of pre-validated filename
* assumes a filename of YYYYMMDD.txt
* returns a list:
[0] 'YYYY'
[1] 'MM'
[2] 'DD'
'''
rawdate = os.path.splitext(os.path.basename(file))[0]
date = [rawdate[0:4], rawdate[4:6], rawdate[6:]]
return date