diff --git a/scripts/active_users.sh b/scripts/active_users.sh deleted file mode 100755 index efe4fbb..0000000 --- a/scripts/active_users.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -who | cut -d' ' -f1 | sort -u | wc -l | xargs echo "active_user_count=" | sed 's/ //' diff --git a/scripts/generate_home_page.sh b/scripts/generate_home_page.sh index 5d1935b..cb771ae 100755 --- a/scripts/generate_home_page.sh +++ b/scripts/generate_home_page.sh @@ -1,13 +1,18 @@ #!/bin/bash +# Feed JSON/tdp output from "stats" into mustache template to generate +# tilde.town homepage. Invoke periodically from crontab. + export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games -source /var/local/venvs/tildetown/bin/activate - -stats=/usr/bin/stats -template=/var/www/tilde.town/template.index.html -mustache=/var/local/tildetown/scripts/mustache.hy +template=/usr/local/tildetown-scripts/tildetown/templates/frontpage.html +mustache=/usr/local/tildetown-scripts/tildetown/mustache.py +input_path=/var/www/tilde.town/tilde.json output_path=/var/www/tilde.town/index.html -($stats || echo '{}') | hy $mustache $template > $output_path -deactivate +if [ ! -f "$input_path" ]; then + print "homepage generation needs missing $input_path" + exit 1 +fi + +exec /usr/local/virtualenvs/tildetown/bin/python "$mustache" "$template" < "$input_path" > "$output_path" diff --git a/scripts/stats b/scripts/stats index 29e4b58..1f21b33 100755 --- a/scripts/stats +++ b/scripts/stats @@ -1,8 +1,6 @@ -#!/bin/bash - -cd /var/local - -source venvs/tildetown/bin/activate -output=$(python tildetown/tildetown-py/stats.py) -deactivate -echo $output +#!/bin/sh +# run stats.py in the appropriate virtualenv. +# +# invoke periodically from crontab and direct the output to +# /var/www/tilde.town/tilde.json +exec /usr/local/virtualenvs/tildetown/bin/python /usr/local/tildetown-scripts/tildetown/stats.py diff --git a/tildetown/stats.py b/tildetown/stats.py old mode 100644 new mode 100755 index 7176f8f..2061662 --- a/tildetown/stats.py +++ b/tildetown/stats.py @@ -1,84 +1,169 @@ +#!/usr/local/bin/python3 + +# stats.py - tilde data in tilde data protocol format. +# Copyright 2015 Michael F. Lamb +# License: GPLv3+ + +""" +Outputs JSON data conforming to "~dp (Tilde Description Protocol)" as defined +at: http://protocol.club/~datagrok/beta-wiki/tdp.html + +It is a JSON structure of the form: + +{ + 'name': (string) the name of the server. + 'url': (string) the URL of the server. + 'signup_url': (string) the URL of a page describing the process required to request an account on the server. + 'want_users': (boolean) whether the server is currently accepting new user requests. + 'admin_email': (string) the email address of the primary server administrator. + 'description': (string) a free-form description for the server. + 'users': [ (array) an array of users on the server, sorted by last activity time + { + 'username': (string) the username of the user. + 'title': (string) the HTML title of the user’s index.html page. + 'mtime': (number) a timestamp representing the last time the user’s index.html was modified. + }, + ... + ] + 'user_count': (number) the number of users currently registered on the server. + } + +We also overload this with some data we were using in the previous version of +stats.py, which is of the form: + +{ + 'users': [ (array) of users on the server. + { + 'default': (boolean) Is the user still using their unmodified default index.html? + 'favicon': (string) a url to an image representing the user + }, + ... + ] + 'live_user_count': (number) count of live users (those who have changed their index.html) + 'active_user_count': (number) count of currently logged in users + 'generated_at': (string) the time this JSON was generated in '%Y-%m-%d %H:%M:%S' format. + 'generated_at_msec': (number) the time this JSON was generated, in milliseconds since the epoch. + 'uptime': (string) output of `uptime -p` + +} +Usage: stats.py > /var/www/html/tilde.json +""" + +import datetime import json -from functools import partial -from os import listdir -from os.path import getmtime, join -from datetime import datetime -from sh import find, uptime, who, sort, wc, cut -from tildetown.util import slurp, thread, p +import os +import pwd +import re +import subprocess -# this script emits json on standard out that has information about tilde.town -# users. It denotes who has not updated their page from the default. It also -# reports the time this script was run. The user list is sorted by public_html update time. +SYSTEM_USERS = ['wiki', 'root', 'ubuntu', 'nate', 'nobody'] +DEFAULT_HTML_FILENAME = "/etc/skel/public_html/index.html" +title_re = re.compile(r']*>(.*)', re.DOTALL) -SYSTEM_USERS = ['wiki', 'root', 'ubuntu', 'nate'] +def active_user_count(): + """Return the count of unique usernames logged in.""" + return len(set(line.split()[0] for line in + subprocess.check_output( + ["who"], universal_newlines=True).splitlines())) -DEFAULT_HTML = slurp("/etc/skel/public_html/index.html") +def get_title(indexhtml): + """Given an html file, return the content of its """ + with open(indexhtml, 'rt', errors='ignore') as fp: + title = title_re.search(fp.read()) + if title: + return title.group(1) -username_to_html_path = lambda u: "/home/{}/public_html".format(u) +def get_users(): + """Generate tuples of the form (username, homedir) for all normal + users on this system. -def default_p(username): - return DEFAULT_HTML == slurp(join(username_to_html_path(username), "index.html")) + """ + return ((p.pw_name, p.pw_dir) for p in pwd.getpwall() if + p.pw_uid >= 1000 and + p.pw_shell != '/bin/false' and + p.pw_name not in SYSTEM_USERS) -def bounded_find(path): - # find might return 1 but still have worked fine. - return find(path, "-maxdepth", "3", _ok_code=[0,1]) +def most_recent_within(path): + """Return the most recent timestamp among all files within path, 3 + levels deep. + """ + return max(modified_times(path, maxdepth=3)) -def get_active_user_count(): - return int(wc(sort(cut(who(), "-d", " ", "-f1"), "-u"), "-l")) +def modified_times(path, maxdepth=None): + """Walk the directories in path, generating timestamps for all + files. + """ + for root, dirs, files in os.walk(path): + if maxdepth and len(root[len(path):].split(os.sep)) == maxdepth: + dirs.clear() + for f in files: + try: + yield os.path.getmtime(os.path.join(root, f)) + except (FileNotFoundError, PermissionError): + pass -def guarded_mtime(path): - try: - return getmtime(path.rstrip()) - except Exception as _: - return 0 +def tdp_user(username, homedir): + """Given a unix username, and their home directory, return a TDP format + dict with information about that user. -def modify_time(username): - files_to_mtimes = partial(map, guarded_mtime) - return thread(username, - username_to_html_path, - bounded_find, - files_to_mtimes, - list, - max) + """ + public_html = os.path.join(homedir, 'public_html') + index_html = os.path.join(public_html, 'index.html') + if os.path.exists(index_html): + return { + 'username': username, + 'title': get_title(index_html), + 'mtime': int(most_recent_within(public_html) * 1000), + # tilde.town extensions and backward compatibility + 'favicon': 'TODO', + 'default': subprocess.call( + ['diff', '-q', DEFAULT_HTML_FILENAME, index_html], + stdout=subprocess.DEVNULL) == 0, + } + else: + return { + 'username': username, + 'default': False + } -def sort_user_list(usernames): - return sorted(usernames, key=modify_time) +def tdp(): + now = datetime.datetime.now() + users = sorted( + (tdp_user(u, h) for u, h in get_users()), + key=lambda x:x['mtime'], + reverse=True) -def user_generator(): - ignore_system_users = lambda un: un not in SYSTEM_USERS - return filter(ignore_system_users, listdir("/home")) + # TDP format data + data = { + 'name': 'tilde.town', + 'url': 'http://tilde.town', + 'signup_url': 'http://goo.gl/forms/8IvQFTDjlo', + 'want_users': True, + 'admin_email': 'nks@lambdaphil.es', + 'description': " ".join(l.strip() for l in """ + an intentional digital community for creating and sharing works of + art, educating peers, and technological anachronism. we are a + completely non-commercial, donation supported, and committed to + rejecting false technological progress in favor of empathy and + sustainable computing. + """.splitlines()), + 'user_count': len(users), + 'users': users, + } -def get_user_data(): - username_to_data = lambda u: {'username': u, - 'default': default_p(u), - 'favicon':'TODO'} - live_p = lambda user: not user['default'] + # tilde.town extensions and backward compatibility + data.update({ + 'active_user_count': active_user_count(), + 'generated_at': now.strftime('%Y-%m-%d %H:%M:%S'), + 'generated_at_msec': int(now.timestamp() * 1000), + 'uptime': subprocess.check_output(['uptime', '-p'], universal_newlines=True), + 'live_user_count': sum(1 for x in data['users'] if not x['default']), + }) - all_users = thread(user_generator(), - sort_user_list, - reversed, - partial(map, username_to_data), - list) - - live_users = list(filter(live_p, all_users)) - - active_user_count = get_active_user_count() - - return {'all_users': all_users, - 'num_users': len(all_users), - 'num_live_users': len(live_users), - 'active_user_count': active_user_count, - 'live_users': live_users,} - -def get_data(): - user_data = get_user_data() - data = {'generated_at': datetime.now().strftime('%Y-%m-%d %H:%M:%S'), - 'site_name': 'tilde.town', - 'site_url': 'http://tilde.town', - 'uptime': str(uptime('-p')).rstrip(),} - - data.update(user_data) return data +def main(): + print(json.dumps(tdp(), sort_keys=True, indent=2)) + if __name__ == '__main__': - print(json.dumps(get_data())) + raise SystemExit(main()) diff --git a/tildetown/templates/frontpage.html b/tildetown/templates/frontpage.html new file mode 100644 index 0000000..72c5865 --- /dev/null +++ b/tildetown/templates/frontpage.html @@ -0,0 +1,381 @@ +<html> + <head> + <title>welcome to tilde town! + + + + + +

tilde.town

+

is a small unix server made for sharing

+

+ faq + | sign our guestbook + | signup + | code of conduct + | file help ticket + | donate + | wiki +

+~*~*~* {{active_user_count}} townies active right now *~*~*~ + + + + + + +
+

our esteemed users

+ ({{live_user_count}} / {{user_count}} shown)
+ generated at {{generated_at}}
+ sorted by recent changes
+ +
    + {{#users}} + {{^default}} +
  • + {{username}} +
  • + {{/default}} + {{/users}} +
+ full user list +
+

this site is an intentional digital community for creating and + sharing works of art, educating peers, and technological + anachronism. we are a completely + non-commercial, donation supported, and + committed to rejecting false technological progress in favor of + empathy and sustainable computing.

+ +

stuff you can do here

+
    +
  • geocities style hosting
  • +
  • internal irc network
  • +
  • private usenet constellation
  • +
  • internal mail
  • +
  • command line / unix tutelage
  • +
  • make ascii art
  • +
  • host twitter bots
  • +
  • participate in our communal twitter
  • +
  • generate random poetry (under construction)
  • +
+ + +
+
+ |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|
+ |                         !                        /\    |  X  |
+ |                         !                       //\\   | / \ |
+ |      @@@                ! |~~~~~~~~~~~~~|      ///\\\  |/   \|
+ |     @@@@@              /i\|=============|     ////\\\\ |\   /|
+ |      @@@              /(i)\====== == ===|    /////\\\\\| \ / |
+ |                      /((i))\============|   //////\\\\\\  X  |
+ |                      |% % %|=== ===== ==|   I||||||||||I / \ |
+ |                      |% % %|====== =====|   I||||||||||I/   \|
+ |:::                   |% # %|============|   I||||||||||I\   /|
+ |:::                   |% % %|= ====== ===|   I||||||||||I \ / |
+ |:::                   |% % #|==== ==+++++++++++++|||||||I  X  |
+ |:::HHHHHHHHHHHH       |% % %|=======! ! ! ! ! ! !|||||||I / \ |
+ |:::HHHHHH HHHHH       |% # %|= =====! ! ! ! ! ! !|||||||I/   \|
+ |:::HHHHHHHHHHHH       |# % %|=== ===! ! ! ! ! ! !|||||||I\   /|
+ |:::HHH HHHHHHHH       |% % %|=======! ! ! ! ! ! !|||||||I \ / |
+ |:::HHHHHHTTTTTTTTTTTTTTT % #|== = ==! ! ! ! ! ! !|||||||I  X  |
+ |:::HHHHHHI [] [] [] [] I # #|=======! ! ! ! ! ! !|||||||I / \ |
+ |::: HHHHHI [] [] [] [] I % %|====  =! ! ! ! ! ! !|||||||I/   \|
+ |:::HHHH HI [] [] [] [] I # %|== ====! ! ! ! ! ! !|||||||I\   /|
+ |:::HHHHHHI [] [] [] [] I % %|=======! ! ! ! ! ! !|||||||I \ / |
+ |:::HHHHHHI [] [] [] [] I % #|=== = =! ! ! ! ! ! !|||||||I  X  |
+ |:::HH HHHI [] [] [] [] I % #| ======! ! ! ! ! ! !|||||||I / \ |
+ |:::HHHHHHI [] [] [] [] I # #|= = ===! ! ! ! ! ! !|||||||I/   \|
+ |:::HHHHH I [] [] [] [] I % %|==== ==! ! ! ! ! ! !|||||||I\   /|
+ |:::HHHHHHI [] [] [] [] I % #|=======! ! ! ! ! ! !|||||||I \ / |
+ |:::HHHHHHI [] [] [] [] I # #|= =====! ! ! ! ! ! !|||||||I  X  |
+ |:::H HHHHI [] [] [] [] I # #|=== = =! ! ! ! ! ! !|||||||I / \ |
+ |:::HHHHHHI [] [] [] [] I # #|=======! ! ! ! ! ! !|||||||I/   \|
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+              
+    o  | |    |                               
+_|_    | |  __|   _  _|_  __           _  _   
+ |  |  |/  /  |  |/   |  /  \_|  |  |_/ |/ |  
+ |_/|_/|__/\_/|_/|__/o|_/\__/  \/ \/    |  |_/
+              
+
+
+
+orators, singers, musicians to come!
+ She will break it to you better than I can.
+ Saturn expelled his father, and seized his throne
+-- I wish our opinions were the same.
+ Ere departing fade from my eyes your forests of bayonets
+ Emma soon recollected, and understood him
+
+--'''' My dear papa, he is three-and-twenty.
+ I hope time has not made you less willing to pardon.
+; Nay-be calm, for I am so: Does it burn?
+ She will break it to you better than I can.
+ play the part that looks back on the actor or actress!
+ She continued to look back, but in vain
+
+ It is necessary and beside the large sort is puff.
+; she is living in dread of the visit.
+ Year of comets and meteors transient and strange
+;'' remember that I am here.-- Mr.
+ She will break it to you better than I can.
+; and Emma was quite in charity with him.
+
+ It is a mere nothing after all
+ The doors of the two rooms were just opposite each other.
+ She will break it to you better than I can.
+ I shall just go round by Mrs. Cole 's
+ Here is realization, Here is a man tallied
+ Even pleasure, you know, is fatiguing
+
+ Keep your raptures for Harriet 's face.
+ Ere departing fade from my eyes your forests of bayonets
+ For to one side she leans, Then back she sways
+ It soon appeared that London was not the place for her.
+ Perhaps they had the figure of a winged horse on the prow
+ She will break it to you better than I can.
+
+ She will break it to you better than I can.
+ To conclude, I announce what comes after me.
+; nothing delineated or distinguished.
+ Excelsior Who has gone farthest?
+ To girlhood, boyhood look, the teacher and the school.
+`` How I could so long a time be fancying myself!
+
+`` Yes, sometimes he can.'' Do you mean with regard to this letter?''
+ Any nonsense will serve. the flesh over and over!
+ Eleves, I salute you!`` I not aware!'
+
+
+
+ Have you guess 'd you yourself would not continue?
+ The wanderer would not heed me
+`` He told me at parting, that he should soon write
+ Mr. Elton looked all happiness at this proposition
+ what is it to us what the rest do or think?
+ I doubted it more the next day on Box Hill
+
+ Come thou, arise from the ground unto the place yonder
+ One was the son of Mars, and was killed before Troy
+ He had done his duty and could return to his son.
+ I never saw any thing so outree!
+; woods, together with mountains, are on fire.
+`` He told me at parting, that he should soon write
+
+`` He told me at parting, that he should soon write
+; and, abhorring the light, they fly { abroad} by night.
+ Can not you guess what this parcel holds?''
+ And these things I see suddenly, what mean they?
+ And you, paid to defile the People
+ Very strongly may be sincerely fainting.
+
+ she cried with a most open eagerness
+`` He told me at parting, that he should soon write
+ Let me hear from you without delay
+ By him she was put in mind of what she might do
+; but Emma 's countenance was as steady as her words.
+ Dear Miss Woodhouse, do advise me.''
+
+ It was certainly ‘barbara’ in the eyes of a Greek
+ One can not creep upon a journey
+ One was the son of Mars, and was killed before Troy
+`` He told me at parting, that he should soon write
+[ 77 and struck the guilty{ draught} from his mouth.
+; The golden sun of June declines, It has not caught her eye.
+
+ Clarke translates ‘miseri senis ore,
+: naught there is Save body, having property of touch.
+ Alas poor boy, he will never be better,
+; but if he wished to do it, it might be done.
+`` He told me at parting, that he should soon write
+ One was the son of Mars, and was killed before Troy
+
+ I knew who it came from. The virgin shrieks aloud
+ To feel the presence of a brave commanding officer But she was always right.
+ I 'd rather you would.'' This was a river of Troy
+
+
+

news

+ +

Systems status

+

Wed Oct 14 13:49:12 PDT 2015

+

+ ~vilmibm released a rewrite to the server's infrastructure yesterday. Nothing should be different, but it'll be much easier to work on new features and consume fewer resources. +

+

+ Currently, the guestbook is down. Usenet has also been disabled for now since no one was using it and it has a nontrivial impact on the system. If you want to talk about bringing usenet back, bring it up in irc <3. +

+ + +

BIRTHDAY! The Town Turns 1

+

Sun Oct 11 18:54:35 UTC 2015

+

+ Exactly a year ago, ~vilmibm gave himself + a birthday present in the form of a small unix server meant for + commnunity and sharing. Now, tilde.town is a thriving community of + feels, peer education and beautiful digital art. +

+

+ If you are not yet a towner, check out our code of conduct, + look at some user pages, and sign + up!. Everyone is welcome here regardless of skill level and if + making SSH keys is a little scary, that's okay. Reach out to for help. +

+

+ Here's to another year of html and feels. +

+ +

new stuff!

+

Sat Aug 1 17:27:15 PDT 2015

+

+ today was a day of things happening! +

    +
  • the first ever tilde.town volunteer admin meeting happened today. ~insom, ~krowbar, and ~vilmibm all met to talk about the future tilde.town and how to ease the burden of things like user signups. Marks the first ever international face-to-face meeting of townies. exciting! +
  • +
  • + guestbook went live! (lol) +
  • +
  • + you can now click on the pretty ascii tilde.town over to the + left to visit a random page (or just go directly + to /cgi/random). +
  • +
+ as always, tilde.town is a place for html and feels. feels run + strong and well today. come say hi! +

+

Sun Jul 26 18:01:58 PDT 2015

+

new homepage design

+

Sun Jul 26 18:01:58 PDT 2015

+

+ ~vilmibm made a cleaned up homepage. it's linking to two under construction feature--a new help desk for admin requests and a random page linker. there is also a new FAQ. +

+

+ we've also hit a cool donation milestone of $60. thank you + everyone who has donated! our monthly costs are now covered and if + we hit $65 we can do some more server enhancements. +

+ +

donations! also, new disk

+

Wed Apr 29 06:04:58 UTC 2015

An exciting day for + tilde.town. We now have way to donate via patreon. Thanks to all the + folx who have already become patrons! y'all make the heart of this sysadmin + warm and fuzz. A big part of the push for donations was a need for more server + space which we have as of tonight's server upgrade/reboot. The main tilde.town + server now has a 40gb disk. Enjoy!

+ +

Some updates coming down the pipe: +

    +
  • 2gb quota for all users
  • +
  • fixed poetry server
  • +
  • better signup process/docs
  • +
+

+ +

welcome haiku

+

Thu Mar 5 21:19:42 UTC 2015

+

If you signed up in the last two months or so you had a bum welcome present + in your /home. the script has been fixed and now everyone retroactively has a + welcome haiku in their home directory. enjoy.

+ +

server restart

+

Tue Jan 27 21:13:27 UTC 2015

+

Hi. Today tilde.town had to restart to pick up a patch for CVE 2015-0235. + data.tilde.town (our poetry server) has also been patched and restated. Sorry + for any inconvenience!

+ +

lots of people

+

Thu Jan 1 07:37:16 UTC 2015

+

we have ~370 users. isn't that wild?

+ +

Scheduled Downtime Tonight

+

Wed Nov 26 21:19:12 UTC 2014

+ +

To accommodate our growing user base, there will be a server resize/reboot + tonight (Pacific time). Sadly I cannot give an exact time as I still don't have + internet at my new place and will have to opportunistically find some to + execute this upgrade.

+

The server should only be down for a few minutes, but this will terminate + any running applications (like tmux/screen/irssi). Any files open in an editor + should be preserved by a swap file but you still may want to save and close + things in the next several hours.

+

Also, this upgrade means that tilde.town is no longer an essentially free + project, so I may put up something for donations soon.

+ +

100+ users party report

+

Sun Nov 9 23:07:08 UTC 2014 +

tilde.town now has 102 users. We crossed into three digits last night and + lots of townies hung out in irc to celebrate. Some new features even + launched!

+ +
    +
  • the homepage userlist is now sorted by modified time
  • +
  • there is now a shared
    tweet
    command that all users can use to tweet to the @tildetown account (protip:
    haiku | tweet
    )
  • +
  • run
    random_pokemon.sh
    to get a random ascii art pokemon (thanks, ~sanqui)
  • +
  • a git-based wiki that all townies can edit (thanks for the help, ~datagrok)
  • +
  • an IRC bot (thanks, ~selfsame) that plays numberwang
  • +
  • even moar learnings up on the page of ~shanx
  • +
  • general good feels
  • +
+ +

80 users!

+

Wed Oct 29 18:19:45 UTC 2014 + +

tilde.town just added its 80th user. Welcome! Some folks have been having + trouble with ssh public keys. Remember, public keys are really confusing if you + aren't too familiar with them, and there is no judgement if you can't + immediately get them to work. Feel free to tweet at @tildetown or send an email to ~vilmibm if you'd like some help.

+ +

+ +

60 users

+

Sat Oct 25 19:37:47 UTC 2014 +

tilde.town is up to 60 users. This is lovely! It is also only a tenth of our + planned total user capacity, so if you are interested in a tilde* account we'd + love to have you. You can even submit a user request with a fancy form now!

+ +

+ submit a user account request! +

+

+ +

Change of IP, warnings when you ssh

+

Sun Oct 19 03:28:59 UTC 2014

+

+ In the interest of making sure we don't suddenly disappear from the Internet + as a result of a dynamic IP change on AWS I switched to using an Elastic + IP (amusingly, "elastic" IPs are static). +

+ +

+ The upside is that we now have a static IP (also it's free) (woo). +

+ +

+ The downside is that when you try to SSH into tilde.town, now, you'll get an + error about "something nasty" and there will be many @ characters. You'll want + to edit your .ssh/known_hosts file and remove the entry for tilde.town (your + error message should have a cute copy-pasta-able one-liner for doing this for + you). +

+ +

+ Go ahead and do that, and then log in With Confidence. Hopefully this won't happen again. +

+ +

+ I'll close by mentioning the imminent arrival of many new tildeverse members. We (the tildeverse operators) are just about ready to accept the signups currently languishing in a waitlist. This means lots of new users, which is lovely, but may mean some growing pains while I adapt our server to the new user load in the most cost-effective way. +

+
+ +