From 802412ba224534f02eb017c5013edf0cf44c6f3c Mon Sep 17 00:00:00 2001 From: mio Date: Fri, 21 Apr 2023 02:31:52 +0000 Subject: [PATCH] Initial commit --- .gitignore | 3 + dotfiles/muttrc | 75 +++ dotfiles/tmux.conf | 110 +++++ dotfiles/vimrc | 397 +++++++++++++++ ftg/config.sample.yml | 27 ++ ftg/formatter.py | 272 +++++++++++ ftg/hashi.py | 49 ++ ftg/main.py | 225 +++++++++ ftg/readme.md | 9 + opml/mug.of.opml | 88 ++++ opml/opml.sh | 157 ++++++ opml/readme.md | 30 ++ opml/urls | 11 + readme.md | 10 + saiba80/readme.md | 6 + saiba80/saiba80 | 4 + saiba80/saiba80.py | 22 + saiba80/saiba80_game_titles.py | 31 ++ saiba80/sources/combined.txt | 804 +++++++++++++++++++++++++++++++ saiba80/sources/fashion.txt | 89 ++++ saiba80/sources/films.txt | 214 ++++++++ saiba80/sources/games.txt | 168 +++++++ saiba80/sources/readme.md | 8 + saiba80/sources/toys.txt | 225 +++++++++ saiba80/sources/tv.txt | 108 +++++ thirdparty/.cadastre/home.txt | 13 + thirdparty/.choochoo | 10 + thirdparty/.tracery/8ball | 24 + thirdparty/.tracery/bean | 98 ++++ thirdparty/.tracery/dinner | 107 ++++ thirdparty/.tracery/hello | 29 ++ thirdparty/.tracery/pizza | 181 +++++++ thirdparty/.tracery/tot | 98 ++++ thirdparty/add-roles.txt | 40 ++ thirdparty/madlibs/balls.madlib | 10 + thirdparty/madlibs/chest.madlib | 13 + thirdparty/madlibs/choka.madlib | 13 + thirdparty/madlibs/miles.madlib | 17 + thirdparty/madlibs/plants.madlib | 16 + thirdparty/madlibs/wake.madlib | 10 + thirdparty/nicethings.txt | 8 + thirdparty/our/coffee | Bin 0 -> 101392 bytes thirdparty/our/coffee.nim | 40 ++ thirdparty/our/readme.md | 3 + thirdparty/readme.md | 3 + thirdparty/shrimp.txt | 17 + twtxt/tw2txt | 78 +++ twtxt/txtsh | 537 +++++++++++++++++++++ 48 files changed, 4507 insertions(+) create mode 100644 .gitignore create mode 100644 dotfiles/muttrc create mode 100644 dotfiles/tmux.conf create mode 100644 dotfiles/vimrc create mode 100644 ftg/config.sample.yml create mode 100644 ftg/formatter.py create mode 100644 ftg/hashi.py create mode 100644 ftg/main.py create mode 100644 ftg/readme.md create mode 100644 opml/mug.of.opml create mode 100755 opml/opml.sh create mode 100644 opml/readme.md create mode 100644 opml/urls create mode 100644 readme.md create mode 100644 saiba80/readme.md create mode 100755 saiba80/saiba80 create mode 100644 saiba80/saiba80.py create mode 100644 saiba80/saiba80_game_titles.py create mode 100644 saiba80/sources/combined.txt create mode 100644 saiba80/sources/fashion.txt create mode 100644 saiba80/sources/films.txt create mode 100644 saiba80/sources/games.txt create mode 100644 saiba80/sources/readme.md create mode 100644 saiba80/sources/toys.txt create mode 100644 saiba80/sources/tv.txt create mode 100644 thirdparty/.cadastre/home.txt create mode 100644 thirdparty/.choochoo create mode 100644 thirdparty/.tracery/8ball create mode 100644 thirdparty/.tracery/bean create mode 100644 thirdparty/.tracery/dinner create mode 100644 thirdparty/.tracery/hello create mode 100644 thirdparty/.tracery/pizza create mode 100644 thirdparty/.tracery/tot create mode 100644 thirdparty/add-roles.txt create mode 100644 thirdparty/madlibs/balls.madlib create mode 100644 thirdparty/madlibs/chest.madlib create mode 100644 thirdparty/madlibs/choka.madlib create mode 100644 thirdparty/madlibs/miles.madlib create mode 100644 thirdparty/madlibs/plants.madlib create mode 100644 thirdparty/madlibs/wake.madlib create mode 100644 thirdparty/nicethings.txt create mode 100755 thirdparty/our/coffee create mode 100644 thirdparty/our/coffee.nim create mode 100644 thirdparty/our/readme.md create mode 100644 thirdparty/readme.md create mode 100644 thirdparty/shrimp.txt create mode 100755 twtxt/tw2txt create mode 100755 twtxt/txtsh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2425c5b --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +__pycache__ +*.swp +ftg/config.yml diff --git a/dotfiles/muttrc b/dotfiles/muttrc new file mode 100644 index 0000000..7556651 --- /dev/null +++ b/dotfiles/muttrc @@ -0,0 +1,75 @@ +set realname = "mio" # Name in from field +set from = "mio@tilde.town" # From field +set hostname = "tilde.town" # @[hostname] + +# Folders +set folder = ~/Mail/inbox # mail folder +set spoolfile = /var/mail/mio # mail source folder +set tmpdir = ~/Mail/tmp # Temp folder +set postponed = ~/Mail/drafts # Drafts folder +set record = ~/Mail/sent # Sent folder +set trash = ~/Mail/trash # Trash folder + +# Folders displayed in the sidebar +mailboxes = ~/Mail/inbox +mailboxes = ~/Mail/drafts +mailboxes = ~/Mail/sent +mailboxes = ~/Mail/trash +mailboxes = ~/Mail/town +mailboxes = ~/Mail/list + +# Mark all new as read +macro index A \ + "~NN." \ + "mark all read" + +# Go to folders +macro index,pager gd ~/Mail/drafts "drafts" +macro index,pager gs ~/Mail/sent "sent" +macro index,pager gt ~/Mail/trash "trash" + +# Save messages to folders +bind index,pager f noop +macro index fl "~/Mail/list" +macro index ft "~/Mail/town" + + +bind attach S save-entry # Save attachment +bind compose f edit-from # Edit from field +bind index,pager G group-reply # Group reply +bind index,pager j sidebar-next # Scroll down sidebar +bind index,pager k sidebar-prev # Scroll up sidebar +bind index,pager l list-reply # List reply +bind index,pager o sidebar-open # Open folder listed in sidebar +bind index D purge-message # Delete D flag mail +bind pager next-line # Scroll down in message +bind pager previous-line # Scroll up in message +# For threaded view oldest to newest. Reverse if sorting by newest first. +bind pager previous-undeleted # Previous message +bind pager next-undeleted # Next message + +set sidebar_visible = yes # Show sidebar +set sidebar_width = 20 # Sidebar width +set sidebar_format = "%B%* %?N? [%N]?" # Display: mailbox [new] +set sidebar_short_path = yes # Shorten mailbox paths + +set postpone = yes # Save to drafts +set copy = yes # Save copy of sent mail +set maildir_trash = yes # d flag mail handling +set delete = yes # Move d flag mail on exit +set check_new = yes # Check mail with mailbox open +set mail_check = 600 # Check interval in seconds +set editor = "vim" # Editor to compose mail +set charset = "utf-8" # Character encoding fallback +set send_charset = "utf-8:iso-8859-2:iso-8859-1" # Encoding for sent mail +set content_type = "text/plain" # Message body Content-Type +unset confirmappend # No prompt for mail append + +unmy_hdr * # Clear preset headers +unset user_agent # Disable user-agent header +set hidden_host = yes # Skip hostname +set edit_headers = yes # Allow editing headers +set use_from = yes # Use custom from +set use_domain = yes # Use custom domain +ignore * # Hide all headers except +unignore from date subject to cc # select fields diff --git a/dotfiles/tmux.conf b/dotfiles/tmux.conf new file mode 100644 index 0000000..6e58fbb --- /dev/null +++ b/dotfiles/tmux.conf @@ -0,0 +1,110 @@ +# Keybindings ---------------------------------------------------------------- + +# Prefix +unbind C-b +set-option -g prefix ` +bind-key ` send-prefix + +# Close|new|select windows and sessions +bind X kill-window +bind C-x kill-session +bind n new-window +bind N new-session +bind s choose-window +bind S choose-session + +# Switch windows +bind [ previous-window +bind ] next-window + +# Split windows to panes +bind V split-window -h +bind H split-window -v + +# Navigate panes +bind j select-pane -D +bind k select-pane -U +bind h select-pane -L +bind l select-pane -R + +# Resize panes +bind -r C-j resize-pane -D 5 +bind -r C-k resize-pane -U 5 +bind -r C-h resize-pane -L 5 +bind -r C-l resize-pane -R 5 + +# Reload tmux config +bind R source-file ~/.tmux.conf \; display "Config reloaded" + +# Copy mode +bind c copy-mode +bind p paste-buffer + +# Mouse mode +set -g mouse on + + +# Appearance ----------------------------------------------------------------- + +# Enable 256 colours +set-option -g default-terminal "tmux-256color" + +# Colours +# See https://commons.wikimedia.org/wiki/File:Xterm_256color_chart.svg +CLR1="#dfafff" # magenta +CLR2="#dadada" # light grey 253 +CLR3="#c6c6c6" # light grey 251 +CLR4="#767676" # medium grey 243 +CLR5="#6c6c6c" # medium grey 242 +CLR6="#303030" # dark grey 236 +CLR7="#262626" # dark grey 235 +CLR8="#121212" # dark grey 233 +CLR9="#e4e4e4" # light grey 254 +CLR10="#875f87" # dark magenta + +# Cursor +set-option -ag terminal-overrides ",tmux*:Cr=\E]12;$CLR1\007" + +# Panes +set -g window-style "fg=$CLR2,bg=$CLR6" +set -g window-active-style "fg=$CLR3,bg=$CLR7" + +# Status bar +# Status refresh rate (s) +set -g status-interval 60 +set -g status-bg $CLR8 +set -g status-fg $CLR2 + +# Simple style +# To select a style, uncomment the "set -g" lines under the style +# Uncomment only one set at a time +SM_SESSION=" #[bold]#S " +SM_TIME=" #[bold]%m/%d %H:%M " +SM_USERS=" #(who | sort --key=1,1 --unique | wc -l) townies " +SM_DISK=" ~#(whoami) #(du -hs $HOME | cut -f1 -d ' ') " +SM_MAIL=" #(cat /var/spool/mail/$USER | grep ' ' && echo '✉')" +SM_LEFT="#[fg=$CLR6,bg=$CLR1]$SM_SESSION" +SM_WIN="#[fg=$CLR4,bg=$CLR8] #I #[fg=$CLR2,bg=$CLR8]#W " +SM_WIN_CUR="#[fg=$CLR5,bg=$CLR6] #I #[fg=$CLR1,bg=$CLR6,bold]#W " +SM_RIGHT=" #[fg=$CLR3]$SM_DISK $SM_MAIL $SM_USERS #[fg=$CLR6,bg=$CLR1]$SM_TIME" +set -g status-left "$SM_LEFT" +set -g window-status-format "$SM_WIN" +set -g window-status-current-format "$SM_WIN_CUR" +set -g status-right-length 50 +set -g status-right "$SM_RIGHT" + +# Airline style +AL_SESSION=" #[bold]⛁ #S " +AL_TIME=" ⌚ #[bold]%b %d %H:%M " +AL_USERS=" ⛑ #(who | sort --key=1,1 --unique | wc -l) townies " +AL_DISK=" ⌂ #(du -hs $HOME | cut -f1 -d ' ') " +SM_MAIL=" #(cat /var/spool/mail/$USER | grep ' ' && echo '✉') " +AL_LEFT="#[fg=$CLR6,bg=$CLR1]$AL_SESSION#[fg=$CLR1,bg=$CLR8]" +AL_WIN="#[fg=$CLR4,bg=$CLR8] #I #[fg=$CLR2,bg=$CLR8]#W #[fg=$CLR3,bg=$CLR8,bold] " +AL_WIN_CUR="#[fg=$CLR8,bg=$CLR2,bold]#[fg=$CLR4,bg=$CLR2,bold] #I #[fg=$CLR8]#W #[fg=$CLR2,bg=$CLR8]" +AL_RIGHT="#[fg=$CLR10,bg=$CLR8,bold]#[fg=$CLR9,bg=$CLR10] $AL_DISK $AL_MAIL $AL_USERS #[fg=$CLR1,bg=$CLR10]#[fg=$CLR6,bg=$CLR1,dim]$AL_TIME" +#set -g status-left "$AL_LEFT" +#set -g window-status-format "$AL_WIN" +#set -g window-status-current-format "$AL_WIN_CUR" +#set -g status-right-length 80 +#set -g status-right "$AL_RIGHT" diff --git a/dotfiles/vimrc b/dotfiles/vimrc new file mode 100644 index 0000000..b9a8a20 --- /dev/null +++ b/dotfiles/vimrc @@ -0,0 +1,397 @@ +" Basic settings ------------------------------------------------------------- + +set shell=/bin/bash " Set default shell +set encoding=utf-8 " Set file encoding to UTF-8 +set nomodeline " Disable modeline (run code on file open) +set nocompatible " Reset vi compatibility presets +set backspace=indent,eol,start " Enable backspace key in insert mode + +syntax on " Enable syntax highlighting +set clipboard=unnamedplus " Use system clipboard +set textwidth=79 " Column width to 79 +set colorcolumn=79 " Set column to highlight for textwidth +set wrap " Visual/soft line wrap +set linebreak " Only insert breaks on breakat chars +set nolist " Disable line break, see: +set wrapmargin=0 " vim.wikia.com > tip 989 +set autoindent " Auto-indent on line break +set smartindent " Smart indent +set shiftwidth=2 " Set indentation +set tabstop=2 +set softtabstop=2 +set expandtab " Convert tab to spaces +set shiftround " Indent in multiples of shiftwidth +set cursorline " Highlight current line +set title " Show file title +set number " Show line numbers +set ruler " Show cursor position +set showbreak=↪ " Show line breaks +set showmatch " Show matching opening/closing char +set showcmd " Show command used (bottom bar) +set showmode " Show current mode (bottom bar) +set wildmenu " Enable menu for autocomplete options +set wildmode=list:longest,full " List matches by longest common sections, all +set wildignore=*.tmp,*~ " Exclude some filetypes from wildmenu +set splitbelow " Horizontal split below +set splitright " Vertical split to the right +set hidden " Switch between buffers without save prompt + " To autosave: :set autowrite or autowriteall +set foldenable " Enable code folding +set foldmethod=indent " See :h foldmethod for other options +set foldlevelstart=10 " 0 = all folds closed, 99 = all folds open +set lazyredraw " No redraw when running macros +set cryptmethod=blowfish " Set default encryption method +set history=1000 " Set no. of lines in undo history +set viminfo= " Disable viminfo + " To move viminfo to ~/.vim instead: + " set viminfo+=n~/.vim/viminfo +set noswapfile " Disable *.swp files +set noerrorbells " Disable sounds +set visualbell +set t_vb= +set incsearch " Start searching on input +set nohlsearch " Disble search highlighting +set ignorecase " Ignore case in search for lowercase input +set smartcase " Case-sensitive search for mixed case input +set spelllang=en_gb " Set spell check language +set timeoutlen=2000 " Set timeout (ms) for key mappings +set autochdir " Change into a file's directory on open +colorscheme gruvbox " See :color for options +set background=dark " Set light/dark bg for themes that use it + + +" Highlight trailing whitespace +" https://vi.stackexchange.com/q/8563 +highlight ExtraWhitespace guibg=#870000 ctermbg=088 +match ExtraWhitespace /\s\+$\|\t/ +augroup ExtraWhitespace + au! + au BufWinEnter * match ExtraWhitespace /\s\+$/ + au InsertEnter * match ExtraWhitespace /\s\+\%#\@ key +let mapleader=',' + +" Move to beginning/end of line +nmap b 0 +nmap e $ + +" Move to prev/next visual row +nmap j gj +nmap k gk + +" Keep indent block selected +" https://github.com/bling/dotvim +vmap < >gv + +" Remap ctrl+d to toggle between shell and vim +nmap :sh + +" Disable ctrl+z to avoid accidentally stopping vim +nmap + +" Move page down/up +nmap +nmap + +" Buffer navigation — go to N, prev, next, left, right +nmap bg :ls:b +nmap bh :bprev +nmap bl :bnext +nmap [ h +nmap ] l + +" Buffer width resize — decrease, increase +nmap b- :vertical res -5 +nmap b= :vertical res +5 + +" Buffer loading — close, new (vsplit), reload, new (same split) +nmap bc :bw +nmap bn :enew +nmap br :e +nmap bv v:enew + +" Backup file in current buffer +nmap bk :call BackupFile() + +" Map delete to a black hole register (separate from cut/paste register) +map d "_d + +" Map expression register (used to evaluate expressions) +imap ee = + +" Code folding — collapse all, expand all, toggle current fold +nmap fc zM +nmap fe zR +nmap ft za + +" Git commands +nmap ga :!git add . +nmap gb :!git branch -b +nmap gc :!git commit -m +nmap gca :!git commit +nmap gco :!git checkout +nmap gd :!git diff +nmap gf :!git fetch +nmap gg :!git grep +nmap gl :!git log +nmap gm :!git merge +nmap gph :!git push +nmap gpl :!git pull +nmap gs :!git status + +" New markdown note +nmap md :call AddNewFile('$HOME/', '', 'md') + +" Toggle netrw browser +nmap nt :call ToggleNetrw() + +" Map OmniComplete +imap o + +" Insert paste into file from cat input +" https://stackoverflow.com/a/2545242 +nmap pp :r! cat + +" Search for selection, prompt for replacement, replace all in file +" https://stackoverflow.com/a/31172452 +vnoremap sa "0y:%s/0//g +" Prompt for search/replace text, replace all in selection +vnoremap sr :s///g + +" Toggle spell check +nmap sc :setl spell! + +" Search in current directory +nmap sd :!grep -R + +" Toggle search term highlighting +nmap sh :set nohlsearch! + +" Sessions — load (waits for file input), save +nmap sl :source $HOME/.vim/sessions/ +nmap ss :mksession! $HOME/.vim/sessions/ + +" Tab navigation — close, prev, next, new +" To go directly to tab n: [n]gt +nmap tc :tabc +nmap th :tabp +nmap tl :tabn +nmap tn :tabe + +" Edit/refresh to apply vimrc changes +nmap ve :tabe $MYVIMRC +nmap vr :source $MYVIMRC + +" Show word count +" https://unix.stackexchange.com/a/145293 +nmap wc g +vmap wc :s/\S\+//gn + +" Trim leading whitespace +" https://unix.stackexchange.com/a/29619 +" To reset cursor at first selected line: vmap wsl :%le +vmap wl :normal 0dw + +" Trim trailing whitespace +" http://oualline.com/vim-cook.html#trim +" https://vim.fandom.com/wiki/Remove_unwanted_spaces +nmap wst :1,$s/[ ]*$// +vmap wst :s/\s\+$// + + +" Netrw +" ---------------------------------------------------------------------------- + +" Settings +let g:netrw_banner = 0 " Hide info header +let g:netrw_browse_split = 3 " 0: reuse window, 1: hsplit, 2: vsplit, + " 3: new tab, 4: previous window +let g:netrw_dirhistmax = 0 " 0: disable history/bookmarks +let g:netrw_keepdir = 0 " Sync dir view and change dir paths +let g:netrw_liststyle = 0 " 0: thin, 1: long, 2: wide, 3: tree +let g:netrw_winsize = 25 " Set pane width + +" Set the default path for the scratchpad used by buffer functions +" Default path: ~/.vim/scratchpad +let g:scratch_dir = 'scratchpad' + +" Map keys within the file browser +" https://vonheikemen.github.io/devlog/tools/using-netrw-vim-builtin-file-explorer/ +fun! MapNetrwKeys() + " Toggle hidden file visibility + nmap . gh + " Files — copy, delete, move, rename, select + nmap fc mc + nmap fd D + nmap fm mm + nmap fr R + nmap v mf + " Go back in history + nmap h u + " Close file preview buffer + nmap P z +endfun + +aug netrw_keymaps + au! + autocmd filetype netrw call MapNetrwKeys() +augroup END + +" Toggle the file browser +" https://stackoverflow.com/questions/5006950/setting-netrw-like-nerdtree +fun! g:ToggleNetrw() + Lexplore + vertical resize 25 +endfun + + +" Functions ------------------------------------------------------------------ + +" Trim trailing whitespace (can be used on filetype) +" e.g. au BufWrite *.* :call DeleteExtraWS() +" https://amix.dk/vim/vimrc.html +fun! g:DeleteExtraWS() + exe 'normal mz' + %s/\s\+$//ge + exe 'normal `z' +endfun + + +" Create a new file +fun! g:AddNewFile(path, name, ext) + let date = strftime('%Y-%m-%d') + let fn = '-' . a:name + if a:name == '' + let fn = '' + endif + exe 'tabe' a:path . '/' . date . fn . '.' . a:ext +endfun + + +" Backup the file in the current buffer +" https://www.ibm.com/developerworks/library/l-vim-script-2/index.html +fun! g:BackupFile() + let b:timestamp = strftime('%Y%m%d%H%M%S') + return writefile(getline(1,'$'), bufname('%') . '-' . b:timestamp) +endfun + + +" Wrap a word/selection in brackets +" Based on http://learnvimscriptthehardway.stevelosh.com/chapters/09.html +" and https://superuser.com/a/875160 +let g:WrapWordChars = { + \ '<':'>', '{':'}', '[':']', '(':')', '"':'"', "'":"'", '`':'`', + \ } +fun! g:WrapWord(prefix) + " Temporarily disable Auto Pairs plugin if enabled + if exists('b:autopairs_enabled') + let l:isapenabled = b:autopairs_enabled + let b:autopairs_enabled = 0 + endif + " Add key mappings to prefix + char for insert and select modes + for c in keys(g:WrapWordChars) + exe 'inoremap ' . a:prefix . c . ' viwa' . + \ g:WrapWordChars[c] . 'bi' . c . 'ea' + exe 'vnoremap ' . a:prefix . c . ' xi' . c . + \ g:WrapWordChars[c] . 'P' + endfor + " Restore user settings + if exists('b:autopairs_enabled') + let b:autopairs_enabled = l:isapenabled + endif +endfun +call g:WrapWord('w') + + +" Filetypes ------------------------------------------------------------------ + +filetype plugin indent on + +" Group autocommand calls to avoid duplicates whenever writing to buffer +" http://learnvimscriptthehardway.stevelosh.com/chapters/14.html +" Set indentation by filetype (not set with other basic settings as they will +" override filetype setl whenever vimrc is reloaded) +" shiftwidth: indent/unindent width +" tabstop: tab width in spaces (view) +" softtabstop: tab width in spaces (edit) +augroup Filetypes + au! + + " CSS/Sass + au FileType css,sass,scss setl shiftwidth=2 tabstop=2 softtabstop=2 + \ omnifunc=csscomplete#CompleteCSS + + " Gophermap needs real to convert maps + au BufRead,BufNewFile gophermap setl noexpandtab shiftwidth=4 tabstop=4 + \ softtabstop=0 textwidth=70 colorcolumn=70 + + " HTML + au Filetype htm,html setl + \ foldmethod=indent indentkeys= shiftwidth=2 tabstop=2 softtabstop=2 + + " Markdown + au FileType markdown,md,mkd setl spell shiftwidth=2 tabstop=2 softtabstop=2 + + " Vim-inspired app configs + au BufRead,BufNewFile vifmrc set filetype=vim + au BufRead,BufNewFile vimperatorrc set filetype=vim + au FileType vim,vimrc,vimrc* set shiftwidth=2 tabstop=2 softtabstop=2 +augroup END + + +" Plugins -------------------------------------------------------------------- + +" vim-emoji-complete — insert emoji +let g:emoji_complete_overwrite_standard_keymaps = 0 +imap em (emoji-start-complete) + +" ALE — syntax check/linting +" Run lint manually +let g:ale_lint_on_enter = 0 +let g:ale_lint_on_save = 0 +let g:ale_lint_on_text_changed = 'never' +nmap ld :call ale#cursor#ShowCursorDetail() +nmap li :ALELint +nmap lj (ale_next_wrap) +nmap lk (ale_previous_wrap) + +" Auto Pairs — auto-close brackets +let g:AutoPairs = { + \ '<':'>', '{':'}', '[':']', '(':')', + \ '"':'"', "'":"'", '`':'`', + \ '', + \ '{%':'%}', '{#':'#}', + \ } +" Remap/disable unneeded mappings +let g:AutoPairsMapCR = 0 +let g:AutoPairsMultilineClose = 0 +let g:AutoPairsShortcutFastWrap = '' +let g:AutoPairsShortcutJump = 'aj' +let g:AutoPairsShortcutToggle = 'ap' + +" commentary.vim — code commenting +imap / :Commentary +vmap / :Commentary + +" snippet.vim — code/template expansion +" Key mappings in snipmate.vim/after/plugin/snipMate.vim +let g:snippets_dir = '~/.vim/snippets' +imap . =TriggerSnippet() diff --git a/ftg/config.sample.yml b/ftg/config.sample.yml new file mode 100644 index 0000000..e95c2d3 --- /dev/null +++ b/ftg/config.sample.yml @@ -0,0 +1,27 @@ +home: + dir: "/home/user/public_gopher/ftg" + url: "/~user/ftg" + title: "feed the gopher\n\n" + info: | + A RSS feed service to browse headlines from gopher. All feeds are + unofficial and unrelated to the listed sites. + updated: "Last updated: " + timestamp: "%Y/%m/%d %H:%M %z" + nav_back: "Return to the feeds list" + temp: "The list is currently being updated. Please check again later." + +feeds: + - title: tilde news + url: https://tilde.news/rss + permalink: tilde-news + - title: tildes.net ~tech + url: https://tildes.net/~tech/topics.rss + permalink: tildes-tech + +update: + user_agent: "feedthegopher/0.1" + feed_file: "feed.xml" + hash_file: ".hash" + sleep: 1 + skip_cache: + - youtube.com/watch diff --git a/ftg/formatter.py b/ftg/formatter.py new file mode 100644 index 0000000..3c87fa7 --- /dev/null +++ b/ftg/formatter.py @@ -0,0 +1,272 @@ +# A set of filters to format HTML to text with barebones Markdown-style markup. +import re + + +class HtmlToFText: + + strip_tags1 = { + # Remove comments first, which may wrap around other tags + "", + # Remove classes, ids and extraneous attributes + " class=\"": "\"", " id=\"": "\"", + } + + strip_tags2 = { + # Remove doctype, tags and inner html + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + # Remove non-functional empty links after stripping classes/ids + "", + "": "", + # Remove the tags themselves but not the inner html + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + "", "", + # Remove currently unsupported tags + "", "", + "", "", + "", "", + # "", "", + # "", "", + # "", "", + } + + strip_ws = ["\n\n", "\t", " "] + + format_tags1 = { + ">\n<": "><", + "
": "\n>", + } + + format_tags2 = { + "
": "[[address]]\n", "
": "\n", + "": "*", "": "*", + "": "*", "": "*", + "

": "\n# ", "

": "\n", + "

": "\n## ", "

": "\n", + "

": "\n### ", "

": "\n", + "

": "\n#### ", "

": "\n", + "
": "\n##### ", "
": "\n", + "
": "\n###### ", "
": "\n", + "
": "\n---\n", "
": "\n---\n", "
": "\n---\n", + "
": "\n", "
": "\n", "
": "\n", + "
": "\n>", "
": "\n", + "": "**", "": "**", + "": "`", "": "`", + "": "~~", "": "~~", + "": "**", "": "**", + "
": "\n", "
": "", + "
": "", "
": ": ", + "
": "", "
": "\n", + "
": "*", "
": "*", + "
": "Fig. ", "
": "", + "": "***", "": "***", + "

": "\n", "

": "\n", + "
": "\n```\n", "
": "\n```\n", + "": "«", "": "»", + "": "", "": "", + "": " (", "": ")", + "": "[[~~", "": "~~]]", + "": "**", "": "**", + "": "⏝", "": "⏝", + "": "^", "": "^", + "": "**", "": "**", + "": "__", "": "__", + " ": "\n\n", + "'": "'", + "’": "'", "’": "'", + "“": "\"", "”": "\"", + "–": "—", + "©": "©", + } + + def filter_strip_tags(self, html): + """Strip extraneous html tags.""" + txt = html + # Some tags need to be stripped before others + for tag in self.strip_tags1: + txt = re.sub(tag + ".*?" + self.strip_tags1[tag], "", txt, + flags=re.DOTALL) + for tag in self.strip_tags2: + txt = re.sub(tag + ".*?" + self.strip_tags2[tag], "", txt, + flags=re.DOTALL) + return txt + + def filter_whitespace(self, html): + """Strip extra whitespaces often found in dynamically-generated source + files.""" + txt = html + for ws in self.strip_ws: + txt = txt.replace(ws, "") + return "".join(txt.split("\n\n")) + + def filter_format_tags(self, html): + """Translate select structure and format-related tags to Markdown-like + syntax.""" + txt = html + for tag in self.format_tags1: + txt = txt.replace(tag, self.format_tags1[tag]) + for tag in self.format_tags2: + txt = txt.replace(tag, self.format_tags2[tag]) + return txt + + def filter_img(self, html): + """Translate image tags to Markdown syntax.""" + txt = html + attrs = {"src": "", "title": "", "alt": ""} + imgs = re.findall("", txt, flags=re.DOTALL) + for i in imgs: + for a in attrs: + if (" " + a + "=\"") in i: + attrs[a] = i.split(" " + a + "=\"")[1].split("\"")[0] + elif (" " + a + "='") in i: + attrs[a] = i.split(" " + a + "='")[1].split("'")[0] + elif (" " + a + "=") in i: + attrs[a] = i.split(" " + a + "=")[1].split(" ")[0] + if attrs["title"] != "": + md_link = "![" + attrs["alt"] + "](" + attrs["src"] + " \"" + \ + attrs["title"] + "\")" + else: + md_link = "![" + attrs["alt"] + "](" + attrs["src"] + ")" + txt = txt.replace(i, md_link) + return txt + + def filter_links(self, html): + """Translate links to Markdown syntax.""" + txt = html + links = re.findall("", txt, flags=re.DOTALL) + attrs = {"href": "", "title": ""} + md_link = "" + for l in links: + if " href=\"" in l: + attrs["href"] = l.split(" href=\"")[1].split("\"")[0] + attrs["title"] = l.split(">")[1].strip("") + elif " href='" in l: + attrs["href"] = l.split(" href='")[1].split("'")[0] + attrs["title"] = l.split(">")[1].strip("") + elif " href=" in l: + attrs["href"] = l.split(" href=")[1].split(" ")[0] + attrs["title"] = l.split(">")[1].strip("") + if (attrs["href"] != "") and (attrs["title"] != ""): + md_link = "[" + attrs["title"] + "](" + attrs["href"] + ")" + txt = txt.replace(l, md_link) + return txt + + def filter_embed(self, html): + """Translate embed tags to Markdown links.""" + txt = html + embeds = re.findall("", txt, flags=re.DOTALL) + src = "" + for e in embeds: + if " src=\"" in e: + src = e.split(" src=\"")[1].split("\"")[0] + elif " src='" in e: + src = e.split(" src='")[1].split("'")[0] + elif " src=" in e: + src = e.split(" src=")[1].split(">")[0] + if src != "": + txt = txt.replace(e, "[embed](" + src + ")") + return txt + + def filter_abbr(self, html): + """Format abbr tags, e.g. `HTML` -> `HTML [[abbr: Hypertext Markup Language]]`""" + txt = html + abbrs = re.findall("", txt, flags=re.DOTALL) + attrs = {"abbr": "", "title": ""} + abbrev = "" + for a in abbrs: + if " title=\"" in a: + attrs["title"] = a.split(" title=\"")[1].split("\"")[0] + attrs["abbr"] = a.split(">")[1].strip("") + elif " title='" in a: + attrs["title"] = a.split(" title='")[1].split("'")[0] + attrs["abbr"] = a.split(">")[1].strip("") + elif " title=" in a: + attrs["title"] = a.split(" title=")[1].split(" ")[0] + attrs["abbr"] = a.split(">")[1].strip("") + if (attrs["title"] != "") and (attrs["abbr"] != ""): + abbrev = attrs["abbr"] + "[[abbr: " + attrs["title"] + "]]" + txt = txt.replace(l, abbrev) + return txt + + def filter_time(self, html): + """Format time tags, e.g. `` -> + `Today (1970-01-01)`.""" + txt = html + timestamps = re.findall("", txt) + attrs = {"title": "", "datetime": ""} + for t in timestamps: + attrs["title"] = t.split(">")[1].strip("") + if " datetime=\"" in t: + attrs["datetime"] = t.split(" datetime=\"")[1].split("\"")[0] + elif " datetime='" in t: + attrs["datetime"] = t.split(" datetime='")[1].split("'")[0] + elif " datetime=" in t: + attrs["datetime"] = t.split(" datetime=")[1].split(">")[0] + ts = attrs["title"] + if attrs["datetime"] != "": + ts += " (" + attrs["datetime"] + ")" + txt = txt.replace(t, ts) + return txt + + def filter_ol(self, html): + """Parse ordered lists. Only single-level lists are currently + supported.""" + txt = html + ol = re.findall("
    .*?
", txt, flags=re.DOTALL) + for o in ol: + li = o.replace("", "").replace("", "").split("
  • ") + md = "" + for l in range(1, len(li)): + md += str(l) + ". " + li[l].lstrip() + txt = txt.replace(o, "\n" + md) + return txt + + def filter_ul(self, html): + """Parse unordered lists. Only single-level lists are currently + supported (nested lists will be flattened).""" + txt = html + while ("
      " in txt) or ("
    • " in txt): + txt = txt.replace("
    • ","") + txt = txt.replace("
        ", "\n").replace("
      ", "") + txt = txt.replace("
    • ", "- ").replace("
    • ", "\n") + return txt + + def convert(self, html): + """Run format filters on html string.""" + text = self.filter_strip_tags(html) + text = self.filter_whitespace(text) + text = self.filter_format_tags(text) + text = self.filter_img(text) + text = self.filter_links(text) + text = self.filter_embed(text) + text = self.filter_abbr(text) + text = self.filter_time(text) + text = self.filter_ol(text) + text = self.filter_ul(text) + return text diff --git a/ftg/hashi.py b/ftg/hashi.py new file mode 100644 index 0000000..79c21e0 --- /dev/null +++ b/ftg/hashi.py @@ -0,0 +1,49 @@ +import hashlib +import urllib3 + +import os + + +class Hashi: + + url_headers = {"user-agent": "hashi (a file hash checker)/0.1"} + hash_algorithm = hashlib.sha256() + encoded = "utf-8" + + def __init__(self): + """Initialise libraries.""" + self.http = urllib3.PoolManager(headers=self.url_headers) + urllib3.disable_warnings() + self.hash_update = self.hash_algorithm.update + self.hash_hex = self.hash_algorithm.hexdigest + + def fetch_url(self, url, path): + """Fetch a remote text url and save the contents as an UTF-8 file.""" + resp = self.http.request("GET", url) + os.makedirs(path.rsplit("/", 1)[0], exist_ok=True) + with open(path, "w", encoding=self.encoded) as fh: + fh.write(resp.data.decode(self.encoded)) + + def get_hash(self, file_path): + """Given a text file path, get the hash of the file contents.""" + with open(file_path, "r") as fh: + bf = fh.read() + self.hash_update(bf.encode(self.encoded)) + return self.hash_hex() + + def check_hash(self, file_path, hash_path): + """Compare a file hash with another previously saved hash. Return a + dictionary with a boolean indicating whether the file hash has changed, + the old and new hashes.""" + has_change = {"changed": False, "old": "", "new": ""} + cached_hash = "" + new_hash = self.get_hash(file_path) + try: + with open(hash_path, "r") as fh: + cached_hash = fh.read() + except FileNotFoundError: + # Treat as changed (a call to update) + has_change = {"changed": True, "old": "", "new": new_hash} + if new_hash != cached_hash: + has_change = {"changed": True, "old": cached_hash, "new": new_hash} + return has_change diff --git a/ftg/main.py b/ftg/main.py new file mode 100644 index 0000000..9c28c99 --- /dev/null +++ b/ftg/main.py @@ -0,0 +1,225 @@ +import feedparser +import urllib3 +import yaml + +import os +from shutil import rmtree +from sys import exit +from time import sleep, strftime + +from hashi import Hashi +from formatter import HtmlToFText + + +class FTG: + + def init(self, config): + """Load the config. Please call this first before other methods.""" + self.conf = self.parse_yaml(config) + self.hh = Hashi() + + def run(self): + """Download feeds and generate gophermaps.""" + any_change = False + count = 0 + all_feeds = [] + for f in self.conf["feeds"]: + # Check feed for changes + dir_path = self.conf["home"]["dir"] + "/" + f["permalink"] + feed_path = dir_path + "/" + self.conf["update"]["feed_file"] + hash_path = dir_path + "/" + self.conf["update"]["hash_file"] + self.hh.fetch_url(f["url"], feed_path) + check = self.hh.check_hash(feed_path, hash_path) + # Build a list of feed data to regenerate the home map + f["path"] = feed_path + all_feeds.append(self.parse_rss(f)) + if check["changed"]: + print("Getting update ...") + any_change = True + # Put up placeholder home map while downloading feed items + self.gen_home_map([], mode="temp") + self.parse_file_list(all_feeds[count]["items"], dir_path) + # Cache feed hash + with open(hash_path, "w") as fh: + fh.write(check["new"]) + # Regenerate the map + self.gen_feed_map(all_feeds[count]) + else: + print("Feed is up-to-date.") + count += 1 + sleep(self.conf["update"]["sleep"]) + # If any of the feeds have changed, regenerate the home map + # to ensure the permalinks to feed maps are current + if any_change: + self.gen_home_map(all_feeds) + + def parse_yaml(self, yml): + """Open a YAML file and return a dictionary of values.""" + try: + fh = open(yml, "r") + data = yaml.safe_load(fh) + fh.close() + except: + print("Error: could not load config.") + exit(1) + return data + + def parse_rss(self, feed): + """Given a dictionary with a feed url, title, permalink and feed file + path, parse the url and return a feed data dictionary.""" + if ("url" not in feed) or (feed["url"] == None) or \ + ("permalink" not in feed) or (feed["permalink"] == None): + print("Error: missing/empty field. Please check config.") + exit(1) + try: + print("Parsing " + feed["permalink"] + " ...") + resp = feedparser.parse(feed["path"]) + except: + print("Error: could not parse (" + feed["url"] + ")") + exit(1) + # Insert custom fields + resp["url"] = feed["url"] + resp["permalink"] = feed["permalink"] + if ("title" in feed) and (feed["title"] != None): + resp["display_title"] = feed["title"] + else: + resp["display_title"] = resp["channel"]["title"] + return resp + + def check_filetype(self, url): + """Given a resource url, return a dictionary containing the gopher + filetype and file extension.""" + meta = {} + meta["ext"] = url.rsplit(".", 1)[1] + domain = url.rsplit(".", 1)[0] + if meta["ext"] == "gif": + meta["type"] = "g" + elif (meta["ext"] == "png") or (meta["ext"].lower() == "jpg") or \ + (meta["ext"].lower() == "jpeg"): + meta["type"] = "I" + elif meta["ext"] == "pdf": + meta["type"] = "d" + else: + meta = {"type": "0", "ext": "txt"} + # Return empty meta if site matches keywords list + for kw in self.conf["update"]["skip_cache"]: + if (kw in domain) or (kw in meta["ext"]): + meta = {} + break + return meta + + def get_file(self, url, ext, path): + """Save a link to file given the url, extension and file path.""" + # Initialise urllib and include user-agent with request + hdrs = {"user-agent": self.conf["update"]["user_agent"]} + http = urllib3.PoolManager(headers=hdrs) + # Disable ssl warnings + urllib3.disable_warnings() + resp = http.request("GET", url) + fmt = HtmlToFText() + if ext == "txt": + try: + txt = fmt.convert(resp.data.decode("utf-8")) + with open(path, "w", encoding="utf-8") as fh: + fh.write(txt) + except UnicodeDecodeError: + # Attempt to work around "codec can't decode byte" error + # if certain this is a txt/html file + txt = fmt.convert(resp.data.decode("ISO-8859-1")) + with open(path, "w", encoding="ISO-8859-1") as fh: + fh.write(txt) + else: + try: + with open(path, "wb") as fh: + fh.write(resp.data) + except: + with open(path, "w") as fh: + fh.write("An error occurred while saving the file." + \ + "Please notify the administrator.") + + def parse_file_list(self, file_list, path): + """Given a list of file urls and target directory path, save the links + as files to the path.""" + count = 0 + # Make sure path exists + os.makedirs(path, exist_ok=True) + for i in file_list: + count += 1 + file_meta = self.check_filetype(i["link"]) + if "ext" in file_meta: + print("Downloading item (" + str(count) + "/" + \ + str(len(file_list)) + ") ...") + file_path = path + "/" + str(count) + "." + \ + file_meta["ext"] + self.get_file(i["link"], file_meta["ext"], file_path) + sleep(self.conf["update"]["sleep"]) + + def clear_cache(self, path, *args, **kwargs): + """Given a directory path and removal mode, remove the selections. + Modes: dirs, files, all""" + mode = kwargs.get("mode", "") + if (mode == "dirs") or (mode == ""): + for rt, dirs, files in os.walk(path): + for d in dirs: + rmtree(path + "/" + d) + elif (mode == "files") or (mode == ""): + for rt, dirs, files in os.walk(path): + for f in files: + os.remove(path + "/" + f) + + def gen_home_map(self, feed_data, *args, **kwargs): + """Write the top-level gophermap.""" + if kwargs.get("mode", "") == "temp": + print("Placing temporary gophermap at " + \ + self.conf["home"]["dir"] + " ...") + os.makedirs(self.conf["home"]["dir"], exist_ok=True) + with open(self.conf["home"]["dir"] + "/gophermap", "w") as fh: + fh.write(self.conf["home"]["title"] + \ + self.conf["home"]["info"] + "\r\n" + \ + self.conf["home"]["temp"]) + else: + print("Generating gophermap at " + self.conf["home"]["dir"] + \ + " ...") + os.makedirs(self.conf["home"]["dir"], exist_ok=True) + with open(self.conf["home"]["dir"] + "/gophermap", "w") as fh: + fh.write(self.conf["home"]["title"] + \ + self.conf["home"]["info"] + "\r\n" + \ + self.conf["home"]["updated"] + \ + strftime((self.conf["home"]["timestamp"])) + "\n\n\n") + for f in feed_data: + fh.write("1" + f["display_title"] + "\t" + \ + f["permalink"] + "\n") + + def gen_feed_map(self, feed_data): + """Given a data dictionary for a feed source, write a feed + gophermap.""" + dir_path = self.conf["home"]["dir"] + "/" + feed_data["permalink"] + os.makedirs(dir_path, exist_ok=True) + self.clear_cache(dir_path) + count = 0 + print("Generating gophermap " + feed_data["permalink"] + " ...") + with open(dir_path + "/gophermap", "w") as fh: + # Info text + fh.write(feed_data["display_title"] + "\r\n\n" + \ + "1" + self.conf["home"]["nav_back"] + "\t" + \ + self.conf["home"]["url"] + "\r\n\n" + \ + "hWebsite" + "\tURL:" + feed_data["channel"]["link"] + \ + "\r\n" + "hFeed" + "\tURL:" + feed_data["url"] + "\r\n\n") + # Item links + for i in feed_data["items"]: + count += 1 + fh.write("h" + i["title"] + "\tURL:" + i["link"] + "\r\n") + file_meta = self.check_filetype(i["link"]) + if "ext" in file_meta: + fh.write(file_meta["type"] + "(" + file_meta["ext"] + \ + ")\t" + str(count) + "." + file_meta["ext"] + "\r\n") + if ("author" in i) and (i["author"] != ""): + fh.write("author: " + i["author"] + "\n") + if ("date" in i) and (i["date"] != ""): + fh.write("posted: " + i["date"] + "\n") + fh.write("\n") + + +ftg = FTG() +ftg.init("config.yml") +ftg.run() diff --git a/ftg/readme.md b/ftg/readme.md new file mode 100644 index 0000000..19a98c4 --- /dev/null +++ b/ftg/readme.md @@ -0,0 +1,9 @@ +# feed the gopher + +A RSS feed service to browse headlines from gopher. + + +## Requirements + +- python 3 +- modules: feedparser pyyaml urllib3 diff --git a/opml/mug.of.opml b/opml/mug.of.opml new file mode 100644 index 0000000..7b4885a --- /dev/null +++ b/opml/mug.of.opml @@ -0,0 +1,88 @@ + + + + the most delicious opml on friend planet + Fri, 12 Aug 2022 01:08:10 +0000 + Fri, 12 Aug 2022 01:08:10 +0000 + barista + barista@me.acdw.net + https://tildegit.org/mio/opml + http://dev.opml.org/spec2.html + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/opml/opml.sh b/opml/opml.sh new file mode 100755 index 0000000..f3bf32e --- /dev/null +++ b/opml/opml.sh @@ -0,0 +1,157 @@ +#!/bin/bash +# This script has not been tested with other POSIX shells. + +_whoami="opml.sh" +_npc="" +_readlinkpls=`readlink -f $0` +_whereami=`dirname "$_readlinkpls"` +_wheresauce="$_whereami/urls" +_wheremug="$_whereami/mug.of.opml" +_when_picked=`date -Ru` +_when_tasted="$_when_picked" +_jam_proxy="https://portal.mozz.us/gemini/" +_gob_proxy="https://gopher.tildeverse.org/" + +__twinkle="the most delicious opml on planet earth" +__barista="barista" +__pail="mail@example.tld" +__lid="https://git.tilde.town/mio/scripts/src/branch/main/opml" +__an=" + + + {{twinkle}} + {{when_picked}} + {{when_tasted}} + {{barista}} + {{pail}} + {{lid}} + http://dev.opml.org/spec2.html + + " +__orange=" " +__pecan=" " +__mousse=" " +__latte=" \n" + + +an() { + test -f $_wheresauce || (echo "$_npc sauce or it won't happen" && exit 1) + test -z "$1" || __twinkle="$1" + test -z "$2" || __barista="$2" + test -z "$3" || __pail="$3" + test -z "$4" || __lid="$4" + test -z "$5" || _wheremug="$5" + + # Header + echo -e "$_npc okay, it'll take a few minutes, why don't you get a cuppa in + the meantime? oh wait ..." + echo "$_npc *grabs a mug*" + echo -e "$__an" > $_wheremug.tmp + sed -i "s/{{twinkle}}/$__twinkle/g" $_wheremug.tmp + sed -i "s/{{when_picked}}/$_when_picked/g" $_wheremug.tmp + sed -i "s/{{when_tasted}}/$_when_tasted/g" $_wheremug.tmp + sed -i "s/{{barista}}/$__barista/g" $_wheremug.tmp + sed -i "s/{{pail}}/$__pail/g" $_wheremug.tmp + sed -i "s|{{lid}}|$__lid|g" $_wheremug.tmp +} + +orange_pecan_mousse() { + # Feeds + first_kat="true" + while read lime; do + is_kat=`echo "$lime" | awk '{ print substr($0, 1, 1) }' | grep '\['` + is_char=`echo "$lime" | awk '{ print substr($0, 1, 1) }' | grep -E '\[|#'` + + # Category + if [ -n "$is_kat" ] && [ "$first_kat" == "false" ]; then + echo -e "$__mousse" >> $_wheremug.tmp + fi + if [ -n "$is_kat" ]; then + echo "$_npc *layering mousse*" + first_kat="false" + kat=`echo "$lime" | awk '{ print substr($0, 2, length($0) - 2) }'` + echo -e "$__orange" >> $_wheremug.tmp + sed -i "s/{{kat}}/$kat/" $_wheremug.tmp + fi + + # Feed URL + test -n "$is_char" || seed=`echo "$lime" | awk '{ print $1 }'` + if [ ! -z "$seed" ]; then + # Protocol + echo -e "$_npc *sprinkling* $seed" + is_jam=`echo "$seed" | awk '{ print substr($0, 1, 4) }' | grep 'gem'` + is_gob=`echo "$seed" | awk '{ print substr($0, 1, 4) }' | grep 'gop'` + if [ -n "$is_jam" ]; then + seed=`echo "$seed" | sed "s|gemini://|$_jam_proxy|"` + elif [ -n "$is_gob" ]; then + seed=`echo "$seed" | sed "s|gopher://|$_gob_proxy|"` + fi + germ=`curl -Ls "$seed"` + + is_rss=`echo -e "$germ" | grep -m 1 "/ s/<\/title>.*//1" | + sed "s/.*//1" | sed "s/.*<title type=\"html\">//1" | + sed "s/<!\[CDATA\[//" | sed "s/\]\]>//" | sed "s/ //"` + # RSS + else + sprinkle=`echo -e "$germ" | grep -m 1 "<title>" | + sed "s/.*<title>//" | sed "s/<\/title>.*//" | + sed "s/<!\[CDATA\[//" | sed "s/\]\]>//" | sed "s/ //"` + pearl=`echo -e "$germ" | grep -m 1 "<link>" | + sed "s/.*<link>//" | sed "s/<\/link>.*//" | sed "s/ //"` + luncheon=`echo -e "$germ" | grep -m 1 "<description>" | + sed "s/.*<description>//" | sed "s/<\/description>.*//" | + sed "s/<!\[CDATA\[//" | sed "s/\]\]>//" | sed "s/ //"` + fi + # Guess the website URL from the feed URL to avoid parsing + # strings with multiple link tags and no newline delimiters, + # some feeds don't provide full paths + if [ ! -n "$is_rss" ] || [ "$pearl" == "/" ] || [ "$pearl" == "./" ]; + then + bs=`basename "$seed"` + pearl=`echo -e "$seed" | sed "s/$bs//"` + fi + test -z "$luncheon" && luncheon="No description available" + + echo -e "$__pecan" >> $_wheremug.tmp + sed -i "s|{{sprinkle}}|$sprinkle|g" $_wheremug.tmp + sed -i "s|{{seed}}|$seed|g" $_wheremug.tmp + sed -i "s|{{pearl}}|$pearl|g" $_wheremug.tmp + sed -i "s|{{luncheon}}|$luncheon|g" $_wheremug.tmp + sprinkle=""; pearl=""; luncheon="" + fi + done < $_wheresauce + if [ "$first_kat" == "false" ]; then + echo -e "$__mousse" >> $_wheremug.tmp + fi +} + +latte() { + # Closing + echo -e "$__latte" >> $_wheremug.tmp + mv $_wheremug.tmp $_wheremug + if [ -f $_wheremug ]; then + echo "$_npc your OPML is ready, enjoy!" + else + echo -e "$_npc sorry, something got messed up, lemme know if you want me + to make another one." + rm -r $_wheremug.tmp + fi +} + + +case "$1" in + make) + an "$2" "$3" "$4" "$5" "$6" + orange_pecan_mousse + latte + ;; + *) echo -e "$_npc $_whoami make|welp [title] [author] [author-email] \ +[author-url] [output-file]";; +esac diff --git a/opml/readme.md b/opml/readme.md new file mode 100644 index 0000000..69cf959 --- /dev/null +++ b/opml/readme.md @@ -0,0 +1,30 @@ +# Orange Pecan Mousse Latte + +An OPML generator script for acdw's [friend planet]. +Originally written as a joke at lucidiot's request. +An improved version by acdw is available at the [casa pages] repo. + +[friend planet]: https://acdw.casa/fwends/ +[casa pages]: https://tildegit.org/casa/pages/src/branch/main/opml + + +## Usage + +- Add some RSS feeds to a `urls` file in the same directory as the script. + +- Run the script: + +``` +opml.sh make [title] [author] [author-email] [author-url] [output-file] +``` + +Example: + +``` +opml.sh make "mug of opml" "barista" "barista@dev.null" \ + "https://tildegit.org/casa/pages" ~/mug.of.opml +``` + +## License + +[GCL](https://acdw.casa/gcl/) diff --git a/opml/urls b/opml/urls new file mode 100644 index 0000000..d897861 --- /dev/null +++ b/opml/urls @@ -0,0 +1,11 @@ +# Add RSS feeds here, one per line. Please see example below. + +[podcasts] +# active listening - acdw +https://junk.acdw.net/active_listening/feed.xml +# low tech radio gazette - wsinatra +http://lambdacreate.com/static/ltrg/feed.xml +# tildewhirl - dozens +https://tilde.town/~dozens/podcast/rss.xml +# trashcat tech chat - trashcat +https://podcast.librepunk.club/tctc/ogg.xml diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..28a05e8 --- /dev/null +++ b/readme.md @@ -0,0 +1,10 @@ +# Readme + +Dotfiles, assorted scripts and content made for tilde.town and other tildes. +Some are no longer being developed, but are made available for viewing. People +are welcome to adapt them for their own use. + + +## License + +BSD-3, unless otherwise noted. diff --git a/saiba80/readme.md b/saiba80/readme.md new file mode 100644 index 0000000..d3c71e0 --- /dev/null +++ b/saiba80/readme.md @@ -0,0 +1,6 @@ +# SAIBA-80 + +A simple markovify example to generate group names using lists of 1980s game +titles, toys, films and television series as sources. + +Run: `python3 saiba80.py` diff --git a/saiba80/saiba80 b/saiba80/saiba80 new file mode 100755 index 0000000..0129cd1 --- /dev/null +++ b/saiba80/saiba80 @@ -0,0 +1,4 @@ +#!/bin/sh + +cd ~mio/bin/saiba80 +python3 ./saiba80.py diff --git a/saiba80/saiba80.py b/saiba80/saiba80.py new file mode 100644 index 0000000..0b06067 --- /dev/null +++ b/saiba80/saiba80.py @@ -0,0 +1,22 @@ +import markovify + +with open("sources/combined.txt", "r") as f: + data = f.readlines() + +corpus = "" +for row in data: + corpus += row.strip() + ". " +model = markovify.Text(corpus, state_size=1) + +gen = None +for r in range(1): + while gen == None: + gen = model.make_short_sentence(140) + +remove_chars = [".", ":", "?", "!", ", ", "A ", "Any ", "By ", "Can't ", + "For ", "From ", "In ", "Me ", "Movie", "Of ", "The ", "To ", + "What ", "When ", "Where ", "With ", "You ", "and "] +for chars in remove_chars: + gen = gen.replace(chars, "") + gen = gen.replace(chars.lower(), "") +print(gen) diff --git a/saiba80/saiba80_game_titles.py b/saiba80/saiba80_game_titles.py new file mode 100644 index 0000000..0fb5897 --- /dev/null +++ b/saiba80/saiba80_game_titles.py @@ -0,0 +1,31 @@ +import markovify + +with open("sources/games.txt", "r") as f: + data_games = f.readlines() +with open("sources/toys.txt", "r") as f: + data_toys = f.readlines() +with open("sources/tv.txt", "r") as f: + data_tv = f.readlines() + +corpus_games = "" +for row in data_games: + corpus_games += row.strip() + ". " +model_games = markovify.Text(corpus_games, state_size=1) + +corpus_toys = "" +for row in data_toys: + corpus_toys += row.strip() + ". " +model_toys = markovify.Text(corpus_toys, state_size=1) + +corpus_tv = "" +for row in data_tv: + corpus_tv += row.strip() + ". " +model_tv = markovify.Text(corpus_tv, state_size=1) + +model = markovify.combine([model_games, model_toys, model_tv], [1, 1, 3]) + +gen = None +for r in range(1): + while gen == None: + gen = model.make_short_sentence(50) + print(gen.replace(".", "").replace(":", "")) diff --git a/saiba80/sources/combined.txt b/saiba80/sources/combined.txt new file mode 100644 index 0000000..1edf863 --- /dev/null +++ b/saiba80/sources/combined.txt @@ -0,0 +1,804 @@ +ABM +Akalabeth: World of Doom +Apple-Oids +Armor Attack +Astro Fighter +Astro Invader +Attack Force +Auto Racing +B-1 Nuclear Bomber +Balloon Bomber +Baseball +Basketball +Battlezone +Berzerk +Bill Budge's Space Album +Boxing +Carnival +Cheeky Mouse +Computer Bismarck +Computer Conflict +Conflict 2500 +Cosmic Patrol +Crazy Balloon +Crazy Climber +Dodge 'Em +Dogfight +Dragon-Quest Adventure +Dragster +Eamon +Fishing Derby +Galactic Attack +Galactic Empire +Galactic Trader +Galaxy Invasion +Golf +Gunfighter +Heli Fire +Hellfire Warrior +Horse Racing +Invaders from Space +Invasion From Outer Space +Kaitei Takara Sagashi +King & Balloon +Labyrinth +Lords of Karma +The Mean Checkers Machine +Meta-Trek +Micro-80 Pinball Machine +Midway Campaign +Missile Command +Mission Asteroid +Mission Escape! +Money Madness +Monster Mash & Battleship +Moon Cresta +Musician +Mystery House +N-Sub +NASL Soccer +Navarone +Network +North Atlantic Convoy Raider +Odyssey: The Compleat Apventure +Olympic Decathlon +Pac-Man +Parsector V +Pelé's Soccer +Phoenix +Pinball +Planet Miners +Polaris +The Prisoner +Project Omega +Rally-X +Red Baron +Rescue at Rigel +Rip-Off +Rogue +Sea Battle +Sea Duel +The Search Series +Skiing +Slag +Space and Sport Games +Space Firebird +Space Games-3 +Space Invaders II +Space Monster +Space Panic +Space Zap +Spectar +Star Castle +Star Cruiser +Star Warrior +Stellar Adventure +Stone of Sisyphys +Stratovox +Super Invasion +Spacewar +Super Nova +Tank Battalion +Targ +Time Traveler +Tycoon +Warlords +The Warp Factor +Windfall: The Oil Crisis Game +Winged Samurai +Wizard and the Princess +Wizard of Wor +Zork I +Space Invaders +Galaxian +Lunar Lander +Asteroids +Battlezone +Berzerk +Centipede +Missile Command +Pac-Man +Phoenix +Rally-X +Star Castle +Wizard of Wor +Defender +Tempest +Donkey Kong +Frogger +Scramble +Galaga +Gorf +Ms. Pac-Man +Qix +Vanguard +BurgerTime +Dig Dug +Donkey Kong Junior +Front Line +Joust +Jungle King +Kangaroo +Moon Patrol +Pengo +Pole Position +Q*bert +Robotron 2084 +Gravitar +Time Pilot +Tron +Xevious +Zaxxon +Crystal Castles +Dragon's Lair +Elevator Action +Gyruss +Mappy +Mario Bros. +Sinistar +Spy Hunter +Star Trek +Star Wars +Tapper +Track & Field +1942 +Karate Champ +Kung-Fu Master +Paperboy +Punch-Out!! +Action Force +Action Max +The Adventures of the Galaxy Rangers +Aerobie +AG Bear +Alphie +American Girl +Arcadia 2001 +Armatron +Army Ants +Army men +Astro Wars +Atari 2600 +Atari 7800 +Atari Lynx +Atari XEGS +Baby Blinkins +Babyland General Hospital +Bally Astrocade +Bandai LCD Solarpower +Barbie and the Rockers +Barnyard Commandos +Bathing Beauties +Batman +Battle Beasts +Big Trak +Bionic Six +Blackstar +Blue Comet SPT Layzner +Boglin +BraveStarr +Britains Deetail +Cabbage Patch Kids +Capsela +Captain Power and the Soldiers of the Future +Care Bears +The Centurions +Changeables +Chatter Telephone +Chō Kōsoku Galvion +Chogokin +Coleco Gemini +ColecoVision +Computer Warriors +Construx +Convertors +Dancouga +Dangaioh +Defenders of the Earth +Diaclone +Dino-Riders +Domino Rally +Double Loves +Dragon Ball +Dungeons & Dragons Computer Fantasy Game +Eagle Force +Easy-Bake Oven +Entertech +Entex Adventure Vision +Entex Select-A-Game +Etch A Sketch +Family Computer Network System +Ferrorama +Fire Away +Fistful Of Monsters +Food Fighters +G.I. Joe +G.I. Joe: A Real American Hero +Galaga X6 +Galaxian 2 +Game & Watch +Game Boy +Garbage Pail Kids +The Get Along Gang +Get in Shape Girl +The Glo Friends +Glo Worm +Gobots +God Mazinger +Godaikin +Golden Girl and the Guardians of the Gemstones +Götz +Guess Who? +Gund Snuffles +Gundam +Hades Project Zeorymer +He-Man +Hit Stix +Hollywoods +Hot Looks +The Hugga Bunch +The Infaceables +Inhumanoids +Intellivision +Jayce and the Wheeled Warriors +Jem +Jenga +Kenner Star Wars +Keshi +Keypers +Kinkeshi +Kinnikuman +Koosh ball +Lady Lovely Locks +Lazer Tag +Lego Technic +Lolo +M.A.S.K. +Machine Robo +Madballs +Magic 8-Ball +Magical Emi, the Magic Star +Magnavox Odyssey +Majo-kit +Manglor +Maple Town +Masters of the Universe +Maxie's World +Merlin +Micro Change +Micro Machines +Microman +Micronauts +Microvision +Milton +Mini-Munchman +Missing Link +Monchhichi +Monkgomery +MoonDreamers +Mr. Potato Head +Munchman +My Buddy +My Child +My First Sony +My Little Pony +My Pet Monster +N.I.N.J.A.S. +Nerf +Nerf Blaster +Nerful +Nickelodeon Toys +Nintendo Entertainment System +Omnibot +The Other World +Pillow people +Pin Art +Pocket Rockers +Pokémon +Police Academy +Poochie +Popples +Pound Puppies +Power Lords +Powertrack +Princess of Power +Professor's Cube +Puck +Puffalump +Pyraminx +Rainbow Brite +The Real Ghostbusters +Record Breakers: World of Speed +Ring Raiders +Robo Force +Robotix +Rock Lords +Rose Petal Place +Rubik's Clock +Rubik's Cube +Rubik's Magic +Rubik's Revenge +Rubik's Snake +The Saga of Crystar +Sea Wees +Secret Wars +Sectaurs +SG-1000 +She-Ra +SilverHawks +Simon +Skateboard Smack-Ups +Skip-It +Sky Commanders +Slime +Slip 'N Slide +The Smurfs merchandising +Space Pets +Speak & Spell +Spinja +Spiral Zone +Splatter Up +Star Bird +Star Fairies +Starcom: The U.S. Space Force +Starriors +Steel Monsters +Strawberry Shortcake +Super Powers Collection +Super Soaker +Sweet Secrets +Sylvanian Families +Talk 'n Play +The Talking Mickey Mouse +The Talking Mother Goose +Tandy-12 +Teddy Ruxpin +Teenage Mutant Ninja Turtles +Tente +Terebikko +Teresa +ThunderCats +Transformers +Transformers: Victory +TurboGrafx-16 +Vectrex +Video Challenger +Visionaries: Knights of the Magical Light +Wacky WallWalker +Watchimals +WereBear +The Wild Puffalumps +Wrestling Superstars +Wrinkles +Zoids +Berlin Alexanderplatz +Magnum PI +Nightline +Strumpet City +Yes Minister +The Smurfs +Hill Street Blues +The People's Court +Wetten +Dynasty +Postman Pat +Falcon Crest +Only Fools and Horses +Brideshead Revisited +Cheers +Knight Rider +Family Ties +Countdown +Remington Steele +Brookside +The Mysterious Cities of Gold +Chiquilladas +The Snowman +Boys from the Blackstuff +Newhart and St. Elsewhere +The A-Team +The Day After +He-Man and the Masters of the Universe +Fraggle Rock +Reading Rainbow +G.I. Joe: A Real American Hero +Press Your Luck +The Joy of Painting +An Englishman Abroad +Blackadder and Terrahawks +Airwolf +Blue Thunder +Murder, She Wrote +Miami Vice +Who's the Boss? +The Cosby Show +The Transformers +Night Court +Tales from the Darkside +Thomas the Tank Engine and Friends +Santa Barbara +The Jewel in the Crown +MacGyver +Growing Pains +ThunderCats +Neighbours +Mr. Belvedere +Larry King Live +Edge of Darkness +Winter Evening in Gagra +The Max Headroom Show +EastEnders +The Golden Girls +WrestleMania +The Oprah Winfrey Show +Perfect Strangers +Double Dare +Designing Women +Dragon Ball +Casualty +Pee-wee's Playhouse +Saint Seiya +The Singing Detective +Pingu +Full House +Duck Tales +Headbangers Ball +Thirtysomething +Teenage Mutant Ninja Turtles +ChuckleVision +Ramayan +Fireman Sam +Inspector Morse +The Bold and the Beautiful +Married... with Children +Star Trek: The Next Generation +21 Jump Street +Gen Jack Wow! +Roseanne +Mystery Science Theater 3000 +In the Heat of the Night +Murphy Brown +Yo! MTV Raps +Garfield and Friends +America's Most Wanted +The Wonder Years +The Simpsons +Chip 'n Dale Rescue Rangers +Baywatch +Seinfeld +Saved By The Bell +American Gladiators +Wallace and Gromit +Hey Dude +Red Dwarf +America's Funniest Home Videos +Quantum Leap +COPS +Agatha Christie's Poirot +Shining Time Station +Family Matters +Coach +Inside Edition +9 to 5 +The Agency +Airplane! +Alien Dead +Alligator +Altered States +American Gigolo +Antropophagus +Any Which Way You Can +Arrebato Rapture +Atlantic City +The Awakening +Babylon +A Bad Son +Bad Timing +The Baltimore Bullet +Berlin Alexanderplatz +The Big Brawl +The Big Red One +Bizalom +Confidence +The Black Marble +The Blood of Hussain +The Blue Lagoon +The Blues Brothers +Bon Voyage, Charlie Brown +Breaker Morant +Bronco Billy +Brubaker +Caboblanco +Caddyshack +The Candidate +Can't Stop the Music +Carny +Chann Pardesi +A Change of Seasons +The Changeling +Cheech & Chong's Next Movie +Children's Island +La Cicala +The Cicada +City of Women +The Club +Coal Miner's Daughter +The Competition +The Constant Factor +The Crime of Cuenca +Cruising +Cutting It Short +Dadar Kirti +Deeds of My Elder Brother +Death Watch +Delusion +Die Laughing +The Dogs of War +Dostana +Friendship +Elisita +The Elephant Man +Encounters of the Spooky Kind +Every Man for Himself +The Exterminator +Fade to Black +The Falls +Fame +Fatso +The Final Countdown +The First Deadly Sin +First Family +Flash Gordon +The Fog +Foolin' Around +The Formula +Foxes +Friday the 13th +The Gamekeeper +Gamera: Super Monster +Germany +Gilda Live +Gloria +The Gods Must Be Crazy +Good Riddance +The Great Rock 'n' Roll Swindle +Hangar 18 +Health +Heart Beat +He Knows You're Alone +Heaven's Gate +Herbie Goes Bananas +Hero at Large +Hey Babe! +Hirak Rajar Deshe +The Hit +Home Movies +Hopscotch +How to Beat the High Cost of Living +Humanoids from the Deep +The Hunter +The Idolmaker +Inferno +Inside Moves +The Island +It's My Turn +The Jazz Singer +Just Tell Me What You Want +Karz +Kagemusha +Khubsoorat +Klondike Fever +Ladies' Choice +The Last Flight of Noah's Ark +The Last Married Couple in America +The Last Metro +The Last Witness +The Lathe of Heaven +Legend of Tianyun Mountain +The Life and Times of Rosie the Riveter +Little Darlings +Little Miss Marker +The Long Good Friday +The Long Riders +Loulou +Loving Couples +The Magician of Lublin +The Man with Bogart's Face +Manila by Night +McVicar +Melvin and Howard +Midnight Madness +The Mirror Crack'd +Mon oncle d'Amérique +Motel Hell +Mother's Day +The Mountain Men +My Bodyguard +The Nest +Nightkill +Nijinsky +The Ninth Configuration +O Megalexandros +Oh +One Trick Pony +The Orchestra Conductor +Ordinary People +Out of the Blue +Pepi +Palermo or Wolfsburg +Permanent Vacation +The Pilot +Playing for Time +Popeye +Pray TV +Private Benjamin +The Private Eyes +Prom Night +Qurbani +Raging Bull +Resurrection +The Return of the King +Return of the Secaucus 7 +El Retorno del Hombre Lobo +Roadie +Le Roi et l'oiseau +Rockshow +Rough Cut +Rude Boy +Sällskapsresan +The Sea Wolves +See You in the Next War +Seems Like Old Times +Serial +The Shining +Simon +A Small Circle of Friends +Smokey and the Bandit II +Solo Sunny +Somewhere in Time +Special Treatment +Star Wars Episode V: The Empire Strikes Back +Stardust Memories +Stir Crazy +The Stunt Man +Sunday Lovers +Superman II +Terror Train +La terrazza +That Sinking Feeling +The Shining +Those Lips +Times Square +To Love the Damned +Tom Horn +Tribute +Twelve Months +The Unseen +Up the Academy +Urban Cowboy +Used Cars +Virus +Le Voyage en douce +Waiter +The Watcher in the Woods +When Time Ran Out +Where the Buffalo Roam +Wholly Moses! +Who's Singin' Over There? +Why Would I Lie? +Willie & Phil +Windows +Witches' Brew +Xanadu +The Young Master +Yūgure made +Zigeunerweisen +Fiber Helmet +Aviator +Big Hair +Bodysuit +Bolo Tie +Bon Chic Bon +Bowl Cut +Bustier +Cagoule +Cardigan +Cartwheel Hat +Cornrows +Crop Top +Cut-Off +Denim Skirt +Devilock +Devoré +Burnout +Dolphin Shorts +Dreadlocks +Feathered Hair +Fedora +Flattop +Flip Flops +Friendship Bracelet +Gel Bracelet +Goth +Gothic +Grunge +Hair Crimp +Halterneck +Harem Pants +Headband +Heavy Metal +Hi-Top Fade +High-Rise +High-Top +Homburg Hat +Hotpants +Booty Shorts +Induction Cut +Jelly Shoes +Jheri Curl +Kerchief +La Sape +Leather Shirt +Leg Warmer +Leggins +Leotard +M-1965 Field Jacket +Miniskirt +Mohair +Mohawk +Mullet +Muscle Shirt +Mushroom Hat +Newsboy Cap +Nose Pierce +Parachute Pants +Pencil Skirt +Poet Shirt +Polo Shirt +Pork Pie Hat +Power Dressing +Preppy +Punk +PVC +Quiff +Rah-Rah Shirt +Rattail +Safari Boot +Safety Pin +Shoulder Pads +Shutter Shades +Slap Bracelet +Slim-Fit Pants +Slip +Slouch Socks +Stackhat +Sundress +Suspenders +Sweater Vest +Toe Socks +Tracksuit +Trilby +Trucker Hat +Tube Top +Varsity Jacket + diff --git a/saiba80/sources/fashion.txt b/saiba80/sources/fashion.txt new file mode 100644 index 0000000..0cefb29 --- /dev/null +++ b/saiba80/sources/fashion.txt @@ -0,0 +1,89 @@ +Fiber Helmet +Aviator +Big Hair +Bodysuit +Bolo Tie +Bon Chic Bon +Bowl Cut +Bustier +Cagoule +Cardigan +Cartwheel Hat +Cornrows +Crop Top +Cut-Off +Denim Skirt +Devilock +Devoré +Burnout +Dolphin Shorts +Dreadlocks +Feathered Hair +Fedora +Flattop +Flip Flops +Friendship Bracelet +Gel Bracelet +Goth +Gothic +Grunge +Hair Crimp +Halterneck +Harem Pants +Headband +Heavy Metal +Hi-Top Fade +High-Rise +High-Top +Homburg Hat +Hotpants +Booty Shorts +Induction Cut +Jelly Shoes +Jheri Curl +Kerchief +La Sape +Leather Shirt +Leg Warmer +Leggins +Leotard +M-1965 Field Jacket +Miniskirt +Mohair +Mohawk +Mullet +Muscle Shirt +Mushroom Hat +Newsboy Cap +Nose Pierce +Parachute Pants +Pencil Skirt +Poet Shirt +Polo Shirt +Pork Pie Hat +Power Dressing +Preppy +Punk +PVC +Quiff +Rah-Rah Shirt +Rattail +Safari Boot +Safety Pin +Shoulder Pads +Shutter Shades +Slap Bracelet +Slim-Fit Pants +Slip +Slouch Socks +Stackhat +Sundress +Suspenders +Sweater Vest +Toe Socks +Tracksuit +Trilby +Trucker Hat +Tube Top +Varsity Jacket + diff --git a/saiba80/sources/films.txt b/saiba80/sources/films.txt new file mode 100644 index 0000000..1d926ce --- /dev/null +++ b/saiba80/sources/films.txt @@ -0,0 +1,214 @@ +9 to 5 +The Agency +Airplane! +Alien Dead +Alligator +Altered States +American Gigolo +Antropophagus +Any Which Way You Can +Arrebato Rapture +Atlantic City +The Awakening +Babylon +A Bad Son +Bad Timing +The Baltimore Bullet +Berlin Alexanderplatz +The Big Brawl +The Big Red One +Bizalom +Confidence +The Black Marble +The Blood of Hussain +The Blue Lagoon +The Blues Brothers +Bon Voyage, Charlie Brown +Breaker Morant +Bronco Billy +Brubaker +Caboblanco +Caddyshack +The Candidate +Can't Stop the Music +Carny +Chann Pardesi +A Change of Seasons +The Changeling +Cheech & Chong's Next Movie +Children's Island +La Cicala +The Cicada +City of Women +The Club +Coal Miner's Daughter +The Competition +The Constant Factor +The Crime of Cuenca +Cruising +Cutting It Short +Dadar Kirti +Deeds of My Elder Brother +Death Watch +Delusion +Die Laughing +The Dogs of War +Dostana +Friendship +Elisita +The Elephant Man +Encounters of the Spooky Kind +Every Man for Himself +The Exterminator +Fade to Black +The Falls +Fame +Fatso +The Final Countdown +The First Deadly Sin +First Family +Flash Gordon +The Fog +Foolin' Around +The Formula +Foxes +Friday the 13th +The Gamekeeper +Gamera: Super Monster +Germany +Gilda Live +Gloria +The Gods Must Be Crazy +Good Riddance +The Great Rock 'n' Roll Swindle +Hangar 18 +Health +Heart Beat +He Knows You're Alone +Heaven's Gate +Herbie Goes Bananas +Hero at Large +Hey Babe! +Hirak Rajar Deshe +The Hit +Home Movies +Hopscotch +How to Beat the High Cost of Living +Humanoids from the Deep +The Hunter +The Idolmaker +Inferno +Inside Moves +The Island +It's My Turn +The Jazz Singer +Just Tell Me What You Want +Karz +Kagemusha +Khubsoorat +Klondike Fever +Ladies' Choice +The Last Flight of Noah's Ark +The Last Married Couple in America +The Last Metro +The Last Witness +The Lathe of Heaven +Legend of Tianyun Mountain +The Life and Times of Rosie the Riveter +Little Darlings +Little Miss Marker +The Long Good Friday +The Long Riders +Loulou +Loving Couples +The Magician of Lublin +The Man with Bogart's Face +Manila by Night +McVicar +Melvin and Howard +Midnight Madness +The Mirror Crack'd +Mon oncle d'Amérique +Motel Hell +Mother's Day +The Mountain Men +My Bodyguard +The Nest +Nightkill +Nijinsky +The Ninth Configuration +O Megalexandros +Oh +One Trick Pony +The Orchestra Conductor +Ordinary People +Out of the Blue +Pepi +Palermo or Wolfsburg +Permanent Vacation +The Pilot +Playing for Time +Popeye +Pray TV +Private Benjamin +The Private Eyes +Prom Night +Qurbani +Raging Bull +Resurrection +The Return of the King +Return of the Secaucus 7 +El Retorno del Hombre Lobo +Roadie +Le Roi et l'oiseau +Rockshow +Rough Cut +Rude Boy +Sällskapsresan +The Sea Wolves +See You in the Next War +Seems Like Old Times +Serial +The Shining +Simon +A Small Circle of Friends +Smokey and the Bandit II +Solo Sunny +Somewhere in Time +Special Treatment +Star Wars Episode V: The Empire Strikes Back +Stardust Memories +Stir Crazy +The Stunt Man +Sunday Lovers +Superman II +Terror Train +La terrazza +That Sinking Feeling +The Shining +Those Lips +Times Square +To Love the Damned +Tom Horn +Tribute +Twelve Months +The Unseen +Up the Academy +Urban Cowboy +Used Cars +Virus +Le Voyage en douce +Waiter +The Watcher in the Woods +When Time Ran Out +Where the Buffalo Roam +Wholly Moses! +Who's Singin' Over There? +Why Would I Lie? +Willie & Phil +Windows +Witches' Brew +Xanadu +The Young Master +Yūgure made +Zigeunerweisen diff --git a/saiba80/sources/games.txt b/saiba80/sources/games.txt new file mode 100644 index 0000000..2f439dc --- /dev/null +++ b/saiba80/sources/games.txt @@ -0,0 +1,168 @@ +ABM +Akalabeth: World of Doom +Apple-Oids +Armor Attack +Astro Fighter +Astro Invader +Attack Force +Auto Racing +B-1 Nuclear Bomber +Balloon Bomber +Baseball +Basketball +Battlezone +Berzerk +Bill Budge's Space Album +Boxing +Carnival +Cheeky Mouse +Computer Bismarck +Computer Conflict +Conflict 2500 +Cosmic Patrol +Crazy Balloon +Crazy Climber +Dodge 'Em +Dogfight +Dragon-Quest Adventure +Dragster +Eamon +Fishing Derby +Galactic Attack +Galactic Empire +Galactic Trader +Galaxy Invasion +Golf +Gunfighter +Heli Fire +Hellfire Warrior +Horse Racing +Invaders from Space +Invasion From Outer Space +Kaitei Takara Sagashi +King & Balloon +Labyrinth +Lords of Karma +The Mean Checkers Machine +Meta-Trek +Micro-80 Pinball Machine +Midway Campaign +Missile Command +Mission Asteroid +Mission Escape! +Money Madness +Monster Mash & Battleship +Moon Cresta +Musician +Mystery House +N-Sub +NASL Soccer +Navarone +Network +North Atlantic Convoy Raider +Odyssey: The Compleat Apventure +Olympic Decathlon +Pac-Man +Parsector V +Pelé's Soccer +Phoenix +Pinball +Planet Miners +Polaris +The Prisoner +Project Omega +Rally-X +Red Baron +Rescue at Rigel +Rip-Off +Rogue +Sea Battle +Sea Duel +The Search Series +Skiing +Slag +Space and Sport Games +Space Firebird +Space Games-3 +Space Invaders II +Space Monster +Space Panic +Space Zap +Spectar +Star Castle +Star Cruiser +Star Warrior +Stellar Adventure +Stone of Sisyphys +Stratovox +Super Invasion +Spacewar +Super Nova +Tank Battalion +Targ +Time Traveler +Tycoon +Warlords +The Warp Factor +Windfall: The Oil Crisis Game +Winged Samurai +Wizard and the Princess +Wizard of Wor +Zork I +Space Invaders +Galaxian +Lunar Lander +Asteroids +Battlezone +Berzerk +Centipede +Missile Command +Pac-Man +Phoenix +Rally-X +Star Castle +Wizard of Wor +Defender +Tempest +Donkey Kong +Frogger +Scramble +Galaga +Gorf +Ms. Pac-Man +Qix +Vanguard +BurgerTime +Dig Dug +Donkey Kong Junior +Front Line +Joust +Jungle King +Kangaroo +Moon Patrol +Pengo +Pole Position +Q*bert +Robotron 2084 +Gravitar +Time Pilot +Tron +Xevious +Zaxxon +Crystal Castles +Dragon's Lair +Elevator Action +Gyruss +Mappy +Mario Bros. +Sinistar +Spy Hunter +Star Trek +Star Wars +Tapper +Track & Field +1942 +Karate Champ +Kung-Fu Master +Paperboy +Punch-Out!! diff --git a/saiba80/sources/readme.md b/saiba80/sources/readme.md new file mode 100644 index 0000000..b4a005d --- /dev/null +++ b/saiba80/sources/readme.md @@ -0,0 +1,8 @@ +## Sources + +- <https://en.wikipedia.org/wiki/Category:1980_video_games> +- <https://en.wikipedia.org/wiki/Golden_age_of_arcade_video_games> +- <https://en.wikipedia.org/wiki/Category:1980s_toys> +- <https://en.wikipedia.org/wiki/1980s_in_television#1980s> +- <https://en.wikipedia.org/wiki/1980_movies> +- <https://en.wikipedia.org/wiki/Category:1980s_fashion> diff --git a/saiba80/sources/toys.txt b/saiba80/sources/toys.txt new file mode 100644 index 0000000..b270bfb --- /dev/null +++ b/saiba80/sources/toys.txt @@ -0,0 +1,225 @@ +Action Force +Action Max +The Adventures of the Galaxy Rangers +Aerobie +AG Bear +Alphie +American Girl +Arcadia 2001 +Armatron +Army Ants +Army men +Astro Wars +Atari 2600 +Atari 7800 +Atari Lynx +Atari XEGS +Baby Blinkins +Babyland General Hospital +Bally Astrocade +Bandai LCD Solarpower +Barbie and the Rockers +Barnyard Commandos +Bathing Beauties +Batman +Battle Beasts +Big Trak +Bionic Six +Blackstar +Blue Comet SPT Layzner +Boglin +BraveStarr +Britains Deetail +Cabbage Patch Kids +Capsela +Captain Power and the Soldiers of the Future +Care Bears +The Centurions +Changeables +Chatter Telephone +Chō Kōsoku Galvion +Chogokin +Coleco Gemini +ColecoVision +Computer Warriors +Construx +Convertors +Dancouga +Dangaioh +Defenders of the Earth +Diaclone +Dino-Riders +Domino Rally +Double Loves +Dragon Ball +Dungeons & Dragons Computer Fantasy Game +Eagle Force +Easy-Bake Oven +Entertech +Entex Adventure Vision +Entex Select-A-Game +Etch A Sketch +Family Computer Network System +Ferrorama +Fire Away +Fistful Of Monsters +Food Fighters +G.I. Joe +G.I. Joe: A Real American Hero +Galaga X6 +Galaxian 2 +Game & Watch +Game Boy +Garbage Pail Kids +The Get Along Gang +Get in Shape Girl +The Glo Friends +Glo Worm +Gobots +God Mazinger +Godaikin +Golden Girl and the Guardians of the Gemstones +Götz +Guess Who? +Gund Snuffles +Gundam +Hades Project Zeorymer +He-Man +Hit Stix +Hollywoods +Hot Looks +The Hugga Bunch +The Infaceables +Inhumanoids +Intellivision +Jayce and the Wheeled Warriors +Jem +Jenga +Kenner Star Wars +Keshi +Keypers +Kinkeshi +Kinnikuman +Koosh ball +Lady Lovely Locks +Lazer Tag +Lego Technic +Lolo +M.A.S.K. +Machine Robo +Madballs +Magic 8-Ball +Magical Emi, the Magic Star +Magnavox Odyssey +Majo-kit +Manglor +Maple Town +Masters of the Universe +Maxie's World +Merlin +Micro Change +Micro Machines +Microman +Micronauts +Microvision +Milton +Mini-Munchman +Missing Link +Monchhichi +Monkgomery +MoonDreamers +Mr. Potato Head +Munchman +My Buddy +My Child +My First Sony +My Little Pony +My Pet Monster +N.I.N.J.A.S. +Nerf +Nerf Blaster +Nerful +Nickelodeon Toys +Nintendo Entertainment System +Omnibot +The Other World +Pillow people +Pin Art +Pocket Rockers +Pokémon +Police Academy +Poochie +Popples +Pound Puppies +Power Lords +Powertrack +Princess of Power +Professor's Cube +Puck +Puffalump +Pyraminx +Rainbow Brite +The Real Ghostbusters +Record Breakers: World of Speed +Ring Raiders +Robo Force +Robotix +Rock Lords +Rose Petal Place +Rubik's Clock +Rubik's Cube +Rubik's Magic +Rubik's Revenge +Rubik's Snake +The Saga of Crystar +Sea Wees +Secret Wars +Sectaurs +SG-1000 +She-Ra +SilverHawks +Simon +Skateboard Smack-Ups +Skip-It +Sky Commanders +Slime +Slip 'N Slide +The Smurfs merchandising +Space Pets +Speak & Spell +Spinja +Spiral Zone +Splatter Up +Star Bird +Star Fairies +Starcom: The U.S. Space Force +Starriors +Steel Monsters +Strawberry Shortcake +Super Powers Collection +Super Soaker +Sweet Secrets +Sylvanian Families +Talk 'n Play +The Talking Mickey Mouse +The Talking Mother Goose +Tandy-12 +Teddy Ruxpin +Teenage Mutant Ninja Turtles +Tente +Terebikko +Teresa +ThunderCats +Transformers +Transformers: Victory +TurboGrafx-16 +Vectrex +Video Challenger +Visionaries: Knights of the Magical Light +Wacky WallWalker +Watchimals +WereBear +The Wild Puffalumps +Wrestling Superstars +Wrinkles +Zoids diff --git a/saiba80/sources/tv.txt b/saiba80/sources/tv.txt new file mode 100644 index 0000000..c051baa --- /dev/null +++ b/saiba80/sources/tv.txt @@ -0,0 +1,108 @@ +Berlin Alexanderplatz +Magnum PI +Nightline +Strumpet City +Yes Minister +The Smurfs +Hill Street Blues +The People's Court +Wetten +Dynasty +Postman Pat +Falcon Crest +Only Fools and Horses +Brideshead Revisited +Cheers +Knight Rider +Family Ties +Countdown +Remington Steele +Brookside +The Mysterious Cities of Gold +Chiquilladas +The Snowman +Boys from the Blackstuff +Newhart and St. Elsewhere +The A-Team +The Day After +He-Man and the Masters of the Universe +Fraggle Rock +Reading Rainbow +G.I. Joe: A Real American Hero +Press Your Luck +The Joy of Painting +An Englishman Abroad +Blackadder and Terrahawks +Airwolf +Blue Thunder +Murder, She Wrote +Miami Vice +Who's the Boss? +The Cosby Show +The Transformers +Night Court +Tales from the Darkside +Thomas the Tank Engine and Friends +Santa Barbara +The Jewel in the Crown +MacGyver +Growing Pains +ThunderCats +Neighbours +Mr. Belvedere +Larry King Live +Edge of Darkness +Winter Evening in Gagra +The Max Headroom Show +EastEnders +The Golden Girls +WrestleMania +The Oprah Winfrey Show +Perfect Strangers +Double Dare +Designing Women +Dragon Ball +Casualty +Pee-wee's Playhouse +Saint Seiya +The Singing Detective +Pingu +Full House +Duck Tales +Headbangers Ball +Thirtysomething +Teenage Mutant Ninja Turtles +ChuckleVision +Ramayan +Fireman Sam +Inspector Morse +The Bold and the Beautiful +Married... with Children +Star Trek: The Next Generation +21 Jump Street +Gen Jack Wow! +Roseanne +Mystery Science Theater 3000 +In the Heat of the Night +Murphy Brown +Yo! MTV Raps +Garfield and Friends +America's Most Wanted +The Wonder Years +The Simpsons +Chip 'n Dale Rescue Rangers +Baywatch +Seinfeld +Saved By The Bell +American Gladiators +Wallace and Gromit +Hey Dude +Red Dwarf +America's Funniest Home Videos +Quantum Leap +COPS +Agatha Christie's Poirot +Shining Time Station +Family Matters +Coach +Inside Edition diff --git a/thirdparty/.cadastre/home.txt b/thirdparty/.cadastre/home.txt new file mode 100644 index 0000000..83eaea0 --- /dev/null +++ b/thirdparty/.cadastre/home.txt @@ -0,0 +1,13 @@ +2 2 +[===|===|===.==|===|===] +|(o) i ((O))| +| ((O)) /_\ (o) | +| ((o)) _/___\_ | +| (o) \=-=-=-=/ ((o))| +|--------|o o o|-------| +| ` \=+==+==+=/ ` | +| ` |_||-||_| ` | + ` \==+=\=+=/=+==/ ` +| _o_ ||H|[_]|H|| _o_ | +| THT /_\ THT | +[[mio|oim]] [[mio|oim] diff --git a/thirdparty/.choochoo b/thirdparty/.choochoo new file mode 100644 index 0000000..90c4db2 --- /dev/null +++ b/thirdparty/.choochoo @@ -0,0 +1,10 @@ + +_____.=-[]-===-[]-===-[]-=._____ +^\.__o__,,_____,,_____,,__o__./^ + " -uu- -uu- -uu- " + 0 *!* . 8 @ + ~|^ !/* ^.%. |8 |* + _ .,|. *\!. _V.. ;|, .^\_ + |=-----------mmmm-----------=| +==- /==\-/==\-mmmm-/==\-/==\ -== + \__/ \__/ \__/ \__/ diff --git a/thirdparty/.tracery/8ball b/thirdparty/.tracery/8ball new file mode 100644 index 0000000..61e359e --- /dev/null +++ b/thirdparty/.tracery/8ball @@ -0,0 +1,24 @@ +{ + "origin": [ + "it is certain", + "it is decidedly so", + "without a doubt", + "yes — definitely", + "you may rely on it", + "as I see it, yes", + "most likely", + "outlook good", + "yes", + "signs point to yes", + "reply hazy try again", + "ask again later", + "better not tell you now", + "cannot predict now", + "concentrate and ask again", + "don't count on it", + "my reply is no", + "my sources say no", + "outlook not so good", + "very doubtful" + ] +} diff --git a/thirdparty/.tracery/bean b/thirdparty/.tracery/bean new file mode 100644 index 0000000..10776f2 --- /dev/null +++ b/thirdparty/.tracery/bean @@ -0,0 +1,98 @@ +{ + "prefix": [ + "ground-", + "jelly", + "soy" + ], + "suffix": [ + "acre", + "bag", + "ball", + "ery", + "fest", + "land", + "ies", + "pole", + "stalk", + "town" + ], + "adjective": [ + "bambara", + "black", + "black turtle", + "broad", + "bush", + "calypso", + "castor", + "climbing", + "cocoa", + "coffee", + "common", + "cranberry", + "dragon tongue", + "dwarf", + "fava", + "field", + "flageolet", + "French", + "garbanzo", + "green", + "horse", + "hyacinth", + "jack", + "kidney", + "lima", + "lupini", + "moth", + "mung", + "navy", + "pink", + "pinto", + "pole", + "rattlesnake", + "red", + "rice", + "runner", + "shell", + "sword", + "tepary", + "tongue of fire", + "urad", + "vanilla", + "velvet", + "wax", + "white", + "winged", + "yardlong", + "yellow", + "chuckwagon", + "cowboy", + "dilly", + "pickled green", + "sprouted", + "baked", + "boiled", + "popped", + "refried", + "shelled", + "steamed", + "stir-fried" + ], + "dish": [ + "#adjective# bean chilli", + "#adjective# bean chips", + "#adjective# bean pie", + "#adjective# bean salad", + "#adjective# bean casserole", + "#adjective# beans and rice", + "15 #adjective# bean soup", + "Guernsey #adjective# bean jar", + "senate #adjective# bean soup", + "pork and #adjective# beans" + ], + "origin": [ + "#dish#", + "#adjective# #adjective# bean", + "#adjective# #prefix#bean bean#suffix#" + ] +} diff --git a/thirdparty/.tracery/dinner b/thirdparty/.tracery/dinner new file mode 100644 index 0000000..dc37114 --- /dev/null +++ b/thirdparty/.tracery/dinner @@ -0,0 +1,107 @@ +{ + "poultry": [ + "chicken", "chicken breast", "chicken thighs", "duck breast", "quail", + "turkey breast" + ], + "poultry-adj": [ + "grilled", "sautéed", "pan-seared", "roast" + ], + "meat-cut": [ + "bison meat", "brisket", "elk meat", "ham", "lamb chops", "pork", + "porkchops", "ribs", "steak", "rabbit steak", "sirloin steak", + "strip steak", "veal cutlets" + ], + "meat-cut-adj": [ + "barbeque", "grilled", "pan-seared", "roast" + ], + "mollusc": [ + "clams", "mussels" + ], + "mollusc-adj": [ + "baked", "boiled", "grilled", "steamed", "stir-fried" + ], + "potato-adj": [ + "baked", "mashed", "scalloped", "smashed" + ], + "rice-adj": [ + "arborio", "basmati", "brown", "fried", "glutinous", "long-grain", + "jasmine", "steamed", "sticky", "white", "wild" + ], + "pasta": [ + "angel hair pasta", "linguini", "macaroni", "penne", "spaghetti" + ], + "pasta-meat": [ + "chicken", "meatballs", "parma ham", "prosciutto" + ], + "pasta-sauce": [ + "alfredo sauce", "pesto alla calabrese", "pesto alla genovese", + "pesto alla siciliana", "tomato sauce" + ], + "pasta-pair": [ + "gnocchi with truffle", "gnudi with ricotta", "three-cheese lasagna", + "beef ravioli", "cheese ravioli", "tortellini" + ], + "soup-adj": [ + "chicken", "chicken ginseng", "chicken noodle", "chickpea", + "cream of broccoli", "cream of mushroom", "cream of tomato", "egg drop", + "fisherman's", "french onion", "hot and sour", "leek", "lentil", + "tom yum", "yellow pea" + ], + "soup-side": [ + "a #salad# salad", "a side of crusty bread", "some garlic bread", + "a baguette" + ], + "soup": [ + "#soup-adj# soup", "avgolemono", "butajiru", "clam chowder", + "corn chowder", "gazpacho", "goulash", "haddock chowder", "lobster bisque", + "minestrone", "squash bisque", "potato and corn chowder", "pozole", "soto", + "stracciatella" + ], + "stew-adj": [ + "beef", "curry beef", "fish", "mixed vegetable", "venison" + ], + "salad": [ + "caesar", "couscous", "kale", "mango", "potato", "potato and egg" + ], + "bento-main": [ + "barbeque pork", "poached salmon", "shrimp tempura", "teriyaki beef", + "teriyaki chicken" + ], + "bento-side1": [ + "coleslaw", "pickled ginger", "pickled radishes", "tsukemono", "umeboshi" + ], + "bento-side2": [ + "a rolled egg", "làcháng", "gyoza", "shiitake mushrooms", "seaweed" + ], + "wrap-adj": [ + "bean", "grilled chicken", "shredded pork" + ], + "wrap-side": [ + "avocados", "grilled peppers", "ground beef", "roasted peppers", + "adobo sauce", "chili sauce", "grilled steak" + ], + "wrap": [ + "burritos", "pitas", "tacos" + ], + "entree-part-meat": [ + "#poultry-adj# #poultry#", "#meat-cut-adj# #meat-cut#", + "#mollusc-adj# #mollusc#" + ], + "entree-part-veggie": [ + "artichokes", "arugula", "asparagus", "baby bok choy", "broccoli", + "brussel sprouts", "baby corn", "cauliflower", "kale", "green peas", + "snowpeas", "spinach" + ], + "entree-part-starch": [ + "#potato-adj# potatoes", "#rice-adj# rice" + ], + "origin": [ + "#bento-main# bento with #bento-side1#, #bento-side2# and rice", + "#entree-part-meat# with #entree-part-veggie# and #entree-part-starch#", + "#pasta# with #pasta-meat# and #pasta-sauce#", + "#pasta-pair# and #pasta-sauce#", + "#stew-adj# stew with #rice-adj# rice", + "#soup# with #soup-side#", + "#wrap-adj# wrap with #wrap-side#" + ] +} diff --git a/thirdparty/.tracery/hello b/thirdparty/.tracery/hello new file mode 100644 index 0000000..0d3ca19 --- /dev/null +++ b/thirdparty/.tracery/hello @@ -0,0 +1,29 @@ +{ + "origin": [ + "hello!", + "hi!", + "ahoy!", + "hai", + "EHLO", + "henlo", + "hallo (Dutch)", + "hola (Spanish)", + "salut! (French)", + "ciao! (Italian)", + "tag! (German)", + "hej (Swedish)", + "aloha! (Hawaiian)", + "saluton (Esperanto)", + "هلا (pron: hala)", + "مااس (Arabic, pron: salaam)", + "สวัสดี (Thai, pron: sawatdee)", + "你好 (Chinese, pron: ni hao)", + "こんにちわ (Japanese, pron: konnichiwa)", + "안녕 (Korean, pron: annyeong)", + "नमस्ते (Hindi, pron: namaste)", + "ᐊᐃ (Ai)", + "Привет (Russian, pron: preevyet)", + "שָׁלוֹם (Hebrew, pron: shalom)", + "kia ora (Māori)" + ] +} diff --git a/thirdparty/.tracery/pizza b/thirdparty/.tracery/pizza new file mode 100644 index 0000000..80683fc --- /dev/null +++ b/thirdparty/.tracery/pizza @@ -0,0 +1,181 @@ +{ + "cheese": [ + "asiago", + "bocconcini", + "catupiry", + "cheddar", + "feta", + "fontina", + "goat cheese", + "gorgonzola", + "Monterey Jack", + "mozzarella", + "Oaxaca cheese", + "paneer", + "parmesan", + "provolone", + "ricotta", + "stracchino" + ], + "crust": [ + "deep-dish", + "extra-thick crust", + "mochi crust", + "Neapolitan", + "Roman", + "square", + "thick crust", + "thin crust" + ], + "herb": [ + "basil", + "cilantro", + "dill", + "oregano", + "rosemary" + ], + "meat": [ + "bacon", + "bulgogi beef", + "rendang beef", + "roast beef", + "satay beef", + "achari chicken", + "balado chicken", + "dak galbi", + "grilled chicken", + "rendang chicken", + "rotisserie chicken", + "tandoori chicken", + "teriyaki chicken", + "chicken tikka masala", + "chorizo", + "crocodile", + "emu", + "ham", + "parma ham", + "döner kebab", + "meatballs", + "Peking duck", + "peperoncini", + "pepperoni", + "prosciutto", + "salami", + "sauage", + "German sausage", + "Maltese sausage", + "tripas" + ], + "mushroom": [ + "cremini mushrooms", + "enoki", + "portobello mushrooms", + "shiitake", + "truffle" + ], + "seafood": [ + "anchovies", + "canned tuna", + "clams", + "crab meat", + "crawfish", + "eel", + "lobster", + "mussels", + "oysters", + "poke", + "prawns", + "salmon", + "salmon teriyaki", + "scallops", + "scampi", + "scungilli", + "shrimp", + "tom yum and shrimp", + "smoked salmon", + "squid", + "tuna" + ], + "vegetable": [ + "artichokes", + "arugula", + "asparagus", + "avocado", + "broccoli", + "capers", + "chilis", + "jalapeños", + "serrano chilis", + "corn", + "cucumbers", + "pickled cucumbers", + "kale", + "kimchi", + "green olives", + "onions", + "red onions", + "green peppers", + "red peppers", + "red bell peppers", + "peas", + "potatoes", + "sweet potatoes", + "tomatoes", + "sun-dried tomatoes", + "spinach", + "zucchini" + ], + "garnish": [ + "ground black pepper", + "minced garlic", + "melted butter", + "curry", + "marinara", + "olive oil", + "extra virgin olive oil", + "balsamic vinegar", + "barbecue sauce", + "béarnaise sauce", + "brown sauce", + "donair sauce", + "honey barbecue sauce", + "sriracha", + "tabasco sauce", + "tomato sauce", + "white sauce" + ], + "other": [ + "hard-boiled eggs", + "labane", + "nachos", + "poutine", + "bananas", + "pineapples" + ], + "topping": [ + "#cheese#", + "#herb#", + "#meat#", + "#mushroom#", + "#other#", + "#seafood#", + "#vegetable#" + ], + "forecast": [ + "#crust#, #topping# and #topping# with a chance of #topping#", + "lots of #topping#, with #topping# all the way to the #crust#, and a greater chance of #topping# and #topping# throughout", + "a meaty mix of #meat#, #meat# and #meat#", + "a toss-up of #vegetable#, #vegetable# and #vegetable#", + "a dynamic system of #cheese# and #cheese# is moving in towards the #crust#, along with more #cheese#", + "some scattered #vegetable#, high on the #mushroom#, and just a touch of #topping# below", + "a pretty #crust# with lots of #mushroom#, #herb# and #garnish#", + "plenty of #topping# and #garnish#, followed by trace amounts of #herb#", + "#topping#, #topping# and a shower of #garnish#", + "#meat# and #vegetable# followed later by #cheese#", + "expect some #garnish# and #herb# over the #topping# and #topping#", + "starts with some #meat#, then moves into #vegetable# and #mushroom# over the #crust#" + ], + "origin": [ + "Pizza forecast: #forecast#" + ] +} diff --git a/thirdparty/.tracery/tot b/thirdparty/.tracery/tot new file mode 100644 index 0000000..6f17e59 --- /dev/null +++ b/thirdparty/.tracery/tot @@ -0,0 +1,98 @@ +{ + "animal": [ + "bat", + "black cat", + "crow", + "earthworm", + "newt", + "owl", + "scorpion", + "slug", + "snake", + "spider", + "worm" + ], + "char": [ + "frankenstein", + "ghost", + "grim reaper", + "headless horseman", + "jack 'o lantern", + "mummy", + "phantom", + "werewolf", + "witch", + "vampire", + "zombie" + ], + "part": [ + "arm", + "claw", + "eyeball", + "finger", + "hand", + "paw", + "thumb", + "toe" + ], + "trick": [ + "an invisible cauldron of ghoulash", + "a bottle of super-sour brain licker", + "a whoopie cushion filled with Paris air", + "a #animal# doodle on wet paper with invisible ink", + "a bag of fried devil's tongues coated in chocolate habaneros", + "a bag of bulls eyes", + "a bowl of instant intestines with flash-frozen #part#s, #part#s and #part#s", + "an inflatable #char# #part#", + "a jack 'o lantern full of #char# goo", + "a pack of used gum, with the faint outline of a #part# print visible" + ], + "trick-special": [ + "6 bags Barf Sinson's Everyawful Flavour Jellybeans", + "6 boxes of Weirdo Wonka's Newts", + "6 boxes of Weirdo Wonka's neverlasting gobstoppers", + "6 rolls of Lifedestroyer gummies", + "6 drums of Toxic Waste", + "a bag of OH! Horror! mini-bars (666 g)", + "a party-sized box of Dung-king Donuts", + "6 tiny bars of demonically insipid white chocolate", + "a 666 mL can of Chef Boilerdee's #char# heads in pig's blood soup. 5 character heads to collect!", + "a bottle of Eau de Skunk (666 mL)" + ], + "treat": [ + "a bag of whoppers", + "a pack of vegan chocolate mini-bars", + "a bottle of pancake syrup-flavoured supercharged energy drink", + "a large pack of bobblicious bobblegum", + "a bag of beet and sweet potato crisps", + "a bag of rich butterscotch", + "a bag of salty liquorice", + "a bag of pineapple lumps", + "a bag of chocolate fishes", + "a pack of white rabbit candy", + "a bag of king kong milk candy", + "a bag of #animal#-shaped gummies", + "a can of sweet 'n' sour #animal#", + "a bag of lollipops shaped like #char# heads" + ], + "treat-special": [ + "7 boxes of Willy Wonka's everlasting gobstoppers", + "7 rolls of Deathsaver gummies", + "7 large bars of premium angelic dark chocolate", + "7 boxes of dunkin' chocolate cookies 'n' creme donuts", + "7 bags of freshly-made dragon's beard", + "a box of handmade chocolate truffles", + "a large bag of artisan peanut butter brittles", + "a large jar of konpeito in rainbow colours", + "a big jar of fruit-flavoured gumballs", + "a humungous pumpkin full of candy corn" + ], + "origin": [ + "Happy Halloween! Have #trick#!", + "Happy Halloween! Have #treat#!", + "This Hallow's Eve, have #treat# before you leave!", + "All the world's spooky and frightful, here's something more delightful: #treat#", + "Happy Hallawoooooooooo ... A specia- mwuhahahahaha! Here's #trick-special#!", + "Happy Hallosweet! Time for a special treat! Here's #treat-special#!" + ] +} diff --git a/thirdparty/add-roles.txt b/thirdparty/add-roles.txt new file mode 100644 index 0000000..993c280 --- /dev/null +++ b/thirdparty/add-roles.txt @@ -0,0 +1,40 @@ +# 2018-06-19 +# For ~von/public_html/user_story/add_roles.txt + + +major league foosball player +high-flying hula hooper +bottle cap anthropologist +bumper car bouncer +subzero marine adventurer +aeronautic ether diver +electronics embalmer +flamethrowing polka dancer +UFO mechanic +high-glucose jelly architect +steampunk shaman +heirloom tomato pomologist +tcp packet farmer +lolcode archmage +steam-powered rickshaw pilot +asteroid meteorologist +nuclear synergy engineer +rainbow-scented gummy bear +beatbox robot +reliquary reconstructor +yokai doctor +pyrlit bookworm +candy gusher +rangy brid +love3d virus +brush-conducting gimp +supersonic snail racer +tundra trailblazer +graveyard pan piper +prufrock punkroller +cheese tiramisu officiator +swan lake foxtrotter +giant pancake steamroll operator +sine wave spectrum rider +holistic cybrebenedictor +gothic metal bard diff --git a/thirdparty/madlibs/balls.madlib b/thirdparty/madlibs/balls.madlib new file mode 100644 index 0000000..10722e8 --- /dev/null +++ b/thirdparty/madlibs/balls.madlib @@ -0,0 +1,10 @@ +Down at an English {{place}}, one evening {{person name}} was there +When I heard a {{occupation}} {{verb -ing}} underneath the flair + +I've got a lovely bunch of {{noun plural}} +There they are, all {{verb -ing}} in a row +{{adjective}} ones, {{adjective}} ones, some as {{adjective}} as your head +Give 'em a twist, a {{noun}} of the wrist +That's what the {{occupation}} said +--- +(Adapted from I've Got A Lovely Bunch of Coconuts - Danny Kaye) diff --git a/thirdparty/madlibs/chest.madlib b/thirdparty/madlibs/chest.madlib new file mode 100644 index 0000000..4d46149 --- /dev/null +++ b/thirdparty/madlibs/chest.madlib @@ -0,0 +1,13 @@ +Fifteen {{noun plural}} on the {{adjective}} man's chest— +Drink and the {{person}} had done for the rest— +The {{person}} was {{verb -ed}} by the bos'n's pike. +The bos'n {{verb -ed}} with a marlinspike +And Cookey's {{noun}} was marked belike +⁠⁠It had been gripped +⁠⁠⁠By {{noun plural}} ten; +⁠⁠And there they lay, +⁠All ⁠{{adjective}} dead {{noun plural -en}}, +Like {{noun}} in a boozing-ken— +⁠Yo—ho—ho and a bottle of {{beverage}}! +--- +(Adapted from The Dead Man's Chest - Robert Louis Stevenson) diff --git a/thirdparty/madlibs/choka.madlib b/thirdparty/madlibs/choka.madlib new file mode 100644 index 0000000..8d1c131 --- /dev/null +++ b/thirdparty/madlibs/choka.madlib @@ -0,0 +1,13 @@ +What {{noun, 2 syllables}} above, +{{verb, 2 syllables}} earnestly this night? +What {{noun, 2 syllables}} below, +{{verb, 2 syllables}}, in morning languish? +In darkness, {{noun, 2 syllables}} +{{verb, 2 syllables}} the demons at bay +In light, more {{noun, 2 syllables}} +{{verb, 2 syllables}} the angels away +When swayed, the {{noun, 2 syllables}} +{{verb, 2 syllables}} to the children's plight +Whence laid the {{noun, 2 syllables}} +now cannot {{verb, 2 syllables}} nor see +{{verb, 2 syllables}} to dust, the adults wish. diff --git a/thirdparty/madlibs/miles.madlib b/thirdparty/madlibs/miles.madlib new file mode 100644 index 0000000..ef3d1e6 --- /dev/null +++ b/thirdparty/madlibs/miles.madlib @@ -0,0 +1,17 @@ +My true {{noun}} wuz beautiful, +An' my true {{noun}} wuz gay, +But she's taken a trip on a {{adjective}} ship +{{verb -ed}} out to Botany Bay, +An' though she's far away, +I'll never {{verb}} me own true {{noun}}, +Ten thousand miles away! + +Oh, it wuz a summer's mornin', +When last I {{verb -ed}} my Meg +She'd a Government {{noun}} around each hand +An' another one round her leg +As the big {{noun}} left the bay, +"Adieu," she sez, "{{verb}} me, +Ten thousand miles away!" +--- +(Adapted from Ten Thousand Miles Away) diff --git a/thirdparty/madlibs/plants.madlib b/thirdparty/madlibs/plants.madlib new file mode 100644 index 0000000..aabde85 --- /dev/null +++ b/thirdparty/madlibs/plants.madlib @@ -0,0 +1,16 @@ +Little Celery {{verb}} a myrtle, +who was named Tomato and lived in her garden. +Little Celery {{verb}} a basket with turtles, +and visited Tomato often like a {{adjective}} warden. + +One day, Little Celery and Tomato went {{verb -ing}}, +to watch the neighborhood {{noun}} play. +They stopped at a {{place}} for food amid their wandering +There were plantains and buttered consommé, +and cola that swirled in time to {{adjective}} fizzling, +but they didn't {{verb}} any blueberries, to Tomato's dismay. + +"Don't worry!" Celery assured her companion {{adverb -ly}} +They got seeds from the next {{place}} across the fence, +and {{verb}} them in a big plot that bloomed heartily, +Now every year they shared the {{noun}} with all their friends. diff --git a/thirdparty/madlibs/wake.madlib b/thirdparty/madlibs/wake.madlib new file mode 100644 index 0000000..29a1739 --- /dev/null +++ b/thirdparty/madlibs/wake.madlib @@ -0,0 +1,10 @@ +{{person 1}} lived in {{name}} Street +A {{noun}} Irish, {{adverb}} odd; +They'd a beautiful {{noun}} so rich and sweet +And to {{verb}} in the world they {{verb -ed}} a hod. +Now they had a sort o' the {{adjective}} way +With a love of the {{noun}} poor {{person 1}} was born +And to {{verb}} them on with their work each day +They'd a drop of the {{beverage}} ev'ry morn. +--- +(Adapted from Finnegan's Wake) diff --git a/thirdparty/nicethings.txt b/thirdparty/nicethings.txt new file mode 100644 index 0000000..40f6c94 --- /dev/null +++ b/thirdparty/nicethings.txt @@ -0,0 +1,8 @@ +You are the peanut butter to the town's jelly. We fit so well together! +You add sweetness and spice to the communal kitchen. +The town cocktails are so much tastier with your heart's fruit punch. +Your jellybeans add colour and flavour to tilde.town life. +Your presence warms us like a hearty bowl of vegetable soup. +This peer, by any other name, would stay as sweet. /ShakesPear/ +Your bright energy is as invigorating as the finest morning brew. +Your smile brightens mornings like a sweet, refreshing glass of orange juice. diff --git a/thirdparty/our/coffee b/thirdparty/our/coffee new file mode 100755 index 0000000000000000000000000000000000000000..c06373c38b1f192d9d3ec090d0a548ecdc90c121 GIT binary patch literal 101392 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 literal 0 HcmV?d00001 diff --git a/thirdparty/our/coffee.nim b/thirdparty/our/coffee.nim new file mode 100644 index 0000000..d8c9bda --- /dev/null +++ b/thirdparty/our/coffee.nim @@ -0,0 +1,40 @@ +import random + + +var + coffee = @[ + # Hot coffee + "A café americano is an espresso-based drink made by adding hot water.", + "A café de olla is coffee made in earthen clay pots, flavoured with cinnamon and unrefined cane sugar.", + "A café miel is an espresso with steamed milk, cinnamon and honey.", + "A café Touba is a Senegalese ground coffee made by roasting coffee beans with grains of selim.", + "A caffè lungo is an espresso-based drink made with the liquid brewed rather than adding hot water to the espresso.", + "A caffè macchiato is an espresso marked with a small amount of milk.", + "A caffè Medici is a double shot espresso with chocolate syrup and orange peel, and finished with whipped cream.", + "A cappuccino is an espresso made with steamed milk.", + "A double-double is a drip coffee poured over two creams and two sugars.", + "A doppio is a double shot espresso.", + "An espresso is coffee brewed with a machine that extrudes a small amount of hot water or steam through finely ground coffee.", + "A latte is coffee made with espresso and steamed milk.", + "A manilo is made with espresso and a small amount of milk.", + "A melya is coffee with cocoa powder and honey.", + "A mocha or mocaccino is made with espresso, hot milk, and chocolate syrup, dark or milk chocolate.", + "A pedrocchi is a mint coffee made from espresso, fresh cream, mint syrup and a thin layer of cocoa powder.", + "A pocillo is a shot of unsweetened coffee popular in Latin America.", + "A ristretto is traditionally espresso made with half the amount of water.", + # Iced coffee + "A black tie is made by mixing two shots of espresso with Thai iced tea, which includes black tea, orange blossom water, tamarind, sugar, condensed milk or cream.", + "A frappé is an iced coffee covered with a layer of foam.", + "A guillermo is espresso poured over lime slices, sometimes served with ice or milk.", + "An ice shot is a shot of espresso poured over ice and often with ice cream added on top.", + "A palazzo consists of two shots of chilled espresso mixed with sweet cream.", + "A mazagran is an Algerian cold coffee with ice, and occasionally lemon, rum or sugar.", + "A shakerato is an iced coffee made from espresso shaken with ice cubes.", + # Liqueur coffee + "A caffè corretto is a shot of espresso with a shot of liquor, such as brandy or grappa.", + "An Irish coffee includes Irish whiskey and sugar and topped with cream.", + "A ponce is a shot of espresso with rum and occasionally, lemon zest." + ] + +randomize() +echo sample(coffee) diff --git a/thirdparty/our/readme.md b/thirdparty/our/readme.md new file mode 100644 index 0000000..a4fe920 --- /dev/null +++ b/thirdparty/our/readme.md @@ -0,0 +1,3 @@ +# Scripts for the our bot + +To rebuild a script: `nim c -d:release [script].nim` diff --git a/thirdparty/readme.md b/thirdparty/readme.md new file mode 100644 index 0000000..1d027e1 --- /dev/null +++ b/thirdparty/readme.md @@ -0,0 +1,3 @@ +# Readme + +Content for other townies' applications and bots. diff --git a/thirdparty/shrimp.txt b/thirdparty/shrimp.txt new file mode 100644 index 0000000..a6ad03a --- /dev/null +++ b/thirdparty/shrimp.txt @@ -0,0 +1,17 @@ + ________________ + -------- _______---- -----___ +_---- __---- __________ --, + - __-----` ````---__\\_ + .` / _-' ```------------ + .-|` | (@),..```\```` + . | \ ___,-`===++' `. + . \_ _`--------`````== ===== + . `--_===--' ``=== ==== ======== + .--_____---=====--' ===== ======== ==== + . .======-' ======== =========----. + --_____-'.====--' ===========----. + `. .===-' == ==__. + `--___-''`--___ _________ _-' + `-_ _,' \_________\_ + ```-----/`----______/ + ``--______/ diff --git a/twtxt/tw2txt b/twtxt/tw2txt new file mode 100755 index 0000000..e85b944 --- /dev/null +++ b/twtxt/tw2txt @@ -0,0 +1,78 @@ +#!/bin/bash +_name="tw2txt" +_author="mio" +_desc="download twitter user_timeline.json using twurl and convert to twtxt." +_version="0.1 (2017-03-06)" +_license="BSD-3" + +twurl_src="/1.1/statuses/user_timeline.json" +input="/home/$(whoami)/twitter.json" +output="$(dirname $input)/tw2txt.txt" + +convert() { + if [ -n "$1" ]; then output="$1"; fi + mkdir -p "$(dirname $input)" + twurl "$twurl_src" > $input + + # Concat json, remove [] wrapper + # Split at date start and remove date label, split at date end + # Replace text label with placeholder + # Remove unneeded lines, remove extra newlines + # Replace placeholder + tdata=$(cat $input | tr -d "[]" | \ + sed "s/{\"created_at\":\"/\n/g" | sed "s/\",\"/\n/g" | \ + sed "s/text\":\"/_TWT_/g" | \ + sed "s/.*\":\".*//g" | sed "/^$/d" | \ + sed "N;s/\n_TWT_/\t/g") + rm -rf $input + + # Convert timestamp + local idt + IFS=$old_ifs + IFS=$'\n' + for line in $tdata; do + idt=$(echo $line | cut -f 1) + # Remove any extra newlines from tweet body + # $()\t$() = [timestamp][tab][tweet] + echo -e "$(date -d"$idt" "+%FT%T%:z")\t\ +$(echo -e $line | cut -f 2 | tr -d "\n")" >> $output + done + IFS=$old_ifs + + # Remove escape backslashes from double quotes and urls + sed -i 's/\\"/\"/g' $output + sed -i "s|\\\/|/|g" $output +} + +layout() { + # Convert timestamp + local idt is_date odt + IFS=$old_ifs + IFS=$'\n' + for line in $(cat "$output"); do + idt=$(echo $line | cut -f 1) + # Check if valid date or newline in tweet + # This step should be unneeded after removing \n in convert() + # and is an extra check to avoid date conversion error + is_date=`date -d $idt 2>: 1>:; echo $?` + if [ ! "$is_date" = "1" ]; then + odt=$(date -d"$idt" "+%B %d, %Y %H:%M %Z") + echo -e "$line\n" | sed "s/$idt\t/$odt\n/" + else + echo -e "$line\n" + fi + done + IFS=$old_ifs +} + +case "$1" in + output|-o) convert "$2";; + parse|-p) convert "$output"; cat "$output"; rm -rf "$output";; + help|--help) echo -e "$_name — $_desc\n\n\ +Options:\n\ + output [file]\t\tOutput twtxt to file\n\ + parse\t\t\tView timeline in a parse-friendly format\n\ + --version\t\tShow the version";; + version|--version) echo -e "$_name $_version";; + *) convert "$output"; layout; rm -rf "$output";; +esac diff --git a/twtxt/txtsh b/twtxt/txtsh new file mode 100755 index 0000000..47bad37 --- /dev/null +++ b/twtxt/txtsh @@ -0,0 +1,537 @@ +#!/bin/bash +_name="txtsh" +_author="mio" +_desc="(t[e^]kst\"ish), n. a Bash shell client for twtxt, a microblogging \ +service." +_version="0.3" +_moddate="2017-03-05" +_license="BSD-3" + + +# Defaults +_def_conf="$HOME/.config/$_name/config" + +# Config options +_options=(\ +"nick = user" \ +"twtfile = $HOME/.config/$_name/twtxt.txt" \ +"twturl = https://example.tld/twtxt.txt" \ +"check_following = True" \ +"use_pager = False" \ +"porcelain = False" \ +"disclose_identity = False" \ +"character_limit = 140" \ +"character_warning = 140" \ +"limit_timeline = 20" \ +"timeout = 5.0" \ +"sorting = descending" \ +"pre_tweet_hook = \"\"" \ +"post_tweet_hook = \"\"" \ +# The options below are not part of twtxt spec +"character_limit_on = True" \ +"character_warning_on = True" \ +"limit_search = 20" \ +"editor = vi" \ +) +_registries=(\ +"https://registry.twtxt.org/api/plain/" \ +) + +# (Formatting) Unicode symbols and ANSI escape codes +_fmt_mkr="🞚 " +_fmt_url="⛺ " +_fmt_date="⌚ " +_fmt_bold="\033[1m" +_fmt_reset="\033[0m" +_fmt_ts="+%Y-%m-%d %H:%M %Z" + + +# Create config and data files +create_config() { + mkdir -p "$(dirname $_def_conf)" + + # Replace default values with user input + if [ -n "$1" ]; then _options[0]="nick = $1" + else _options[0]="nick = $(whoami)"; fi + if [ -n "$2" ]; then _options[1]="twtfile = $2"; fi + if [ -n "$3" ]; then _options[2]="twturl = $3"; fi + + # Output config + echo -e "[twtxt]" > $_def_conf + for opt in "${_options[@]}"; do + echo -e "$opt" >> $_def_conf + done + echo -e "\n[following]\n\n[registries]" >> $_def_conf + for opt in "${_registries[@]}"; do + echo -e "$opt" >> $_def_conf + done + + # Create twtfile if it doesn't exist + if [ ! -f "${_options[1]/* = /}" ]; then + mkdir -p "$(dirname ${_options[1]/* = /})" + touch "${_options[1]/* = /}" + fi +} + + +# Load config file +load_config() { + if [ ! -f $_def_conf ]; then + echo -e "Error: no config file found. Try running \ +\"$_name quickstart\" first."; exit 1 + else + # Load config into array + local old_ifs=$IFS + local csection + readarray buffer < $_def_conf + IFS=$'\n' + for line in ${buffer[@]}; do + case "$line" in + \[twtxt\]) csection="twtxt";; + \[following\]) csection="following";; + \[registries\]) csection="registries";; + esac + # Store followings and registries in one variable each + if [ "$csection" = "following" ] && \ + [ ! "$line" = "[following]" ]; then + following="${following}$line\n" + fi + if [ "$csection" = "registries" ] && \ + [ ! "$line" = "[registries]" ]; then + registries=("${registries[@]}" "$line") + fi + # Set variables for the other options + if [ -n "$line" ] && [[ ! "$line" =~ "[" ]] && \ + [ ! "$csection" = "following" ] && \ + [ ! "$csection" = "registries" ]; then + # if: user config value is empty, reset to default value + # else: assign value + if [[ "${line/* = /}" =~ "=" || -z "${line/* = /}" ]] && \ + [ "$csection" = "twtxt" ]; then + for opt in "${_options[@]}"; do + if [[ "$opt" =~ "${line/ */}" ]]; then + eval "${opt/ = */}=${opt/* = /}" + fi + done + else + eval "${line/ = */}=${line/* = /}" + fi + fi + done + IFS=$old_ifs + fi +} + + +# Edit config values +edit_config() { + if [ -z "$1" ]; then + echo -e "Please specify a setting and value."; exit 1 + elif [ -n "$1" ] && [ -n "$2" ]; then + # Edit values + load_config + case "$1" in + nick) sed -i "s|nick = $nick|nick = $2|" $_def_conf;; + twtfile) sed -i "s|twtfile = $twtfile|twtfile = $2|" $_def_conf;; + twturl) sed -i "s|twturl = $twturl|twturl = $twturl|" $_def_conf;; + *) echo -e "See \"$_name config\" for options." + esac + else + # Output current values + load_config + case "$1" in + nick) echo -e "$nick";; + twtfile) echo -e "$twtfile";; + twturl) echo -e "$twturl";; + edit) eval $editor "$_def_conf";; + *) cat $_def_conf;; + esac + fi +} + + +# Wrap curl commands with presets +# to manage flags for all outgoing requests +curlp() { + local ccmd="curl -L -s --connect-timeout $timeout" + + # Include user-agent string with outgoing requests if enabled + if [ "$disclose_identity" = "True" ]; then + ccmd="${ccmd} -A $_name (twtxt)/$_version (+$twturl; @$nick)" + fi + + case $1 in + post) echo -e "$($ccmd -X POST $2)";; + src) echo -e "$($ccmd $2)";; + status) echo -e $($ccmd -I $2 | head -n 1 | cut -f 2 -d " ");; + esac +} + + +# Format a string containing twtxt data for display +format_twdata() { + local tdata ts twt + local old_ifs=$IFS + + # Limit number of results and sort order + case $1 in + search) + tdata=`echo -e "$2" | tail -n $limit_search` + if [ "$sorting" = "descending" ]; then + tdata=$(echo -e "$tdata" | sort -r) + fi;; + timeline*) + tdata=`echo -e "$2" | tail -n $limit_timeline` + if [ "$sorting" = "descending" ]; then + tdata=$(echo -e "$tdata" | sort -r) + fi;; + esac + + # Display data + # Porcelain view + if [ "$porcelain" = "True" ]; then + echo -e "$tdata"; exit 0 + fi + # Formatted view + IFS=$'\n' + for item in ${tdata[@]}; do + case $1 in + search|timeline) + # Format: nick, twturl, timestamp, tweet + # Disable ANSI codes when use_pager is enabled + if [ "$use_pager" = "True" ]; then + _fmt_bold=""; _fmt_reset="" + fi + # Format fields + ts=$(echo ${item} | cut -f 3) + echo -e $_fmt_mkr${_fmt_bold}$(echo ${item} | cut -f 1)\ +${_fmt_reset} + echo -e $_fmt_url$(echo ${item} | cut -f 2) + echo -e $_fmt_date$(date -d$ts $_fmt_ts) + twt=`echo ${item} | cut -f 4` + if [ "$character_limit_on" = "True" ] && \ + [ ${#twt} -gt $character_limit ]; then + twt=$(echo -e "$twt" | cut -c 1-$character_limit) + fi + # Check tweet is not empty string + # e.g. user search has no tweets + if [ -z "$twt" ]; then echo ""; + else echo -e $twt"\n"; fi;; + timeline_me) + # Format: timestamp, tweet + ts=$(echo ${item} | cut -f 1) + echo -e $_fmt_date$(date -d$ts $_fmt_ts) + echo -e $(echo ${item} | cut -f 2)"\n";; + esac + done + IFS=$old_ifs +} + + +# Check for pager before output if available and enabled +# Takes data formatting type and twtxt data string as inputs +output_twdata() { + local fdata + local if_pager=`whereis less | grep "/less"` + if [ "$use_pager" = "True" ] && [ -n "$if_pager" ]; then + fdata=`format_twdata "$1" "$2"` + echo -e "$fdata" > $(dirname $_def_conf)/pager.tmp + less $(dirname $_def_conf)/pager.tmp + rm -rf $(dirname $_def_conf)/pager.tmp + else + format_twdata "$1" "$2" + fi +} + + +# Add a source to following list +follow() { + local is_following=$(echo $following | grep "$2") + if [ -n "$1" ] && [ -n "$2" ] && [ -z "$is_following" ]; then + sed -i "s|\[following\]|\[following\]\n$1\ =\ $2|" $_def_conf + echo -e "You are now following ${_fmt_bold}$1${_fmt_reset}." + elif [ -n "$is_following" ]; then + echo -e "You are already following ${_fmt_bold}$1${_fmt_reset}." + else + echo -e "Usage: $_name follow [nick] [url]" + fi +} + + +# List sources user is following +following() { + if [ -z "$following" ]; then + echo -e "You haven't followed anyone yet." + else + local fol + local fstatus="404" + printf "$following" | while read line; do + # Format: nick @ twturl (status_code) + fol="${_fmt_bold}${line/ = */}${_fmt_reset} @ ${line/* = /}" + if [ "$check_following" = "True" ]; then + fstatus=`curlp "status" "${line/* = /}"` + fol="${fol} ($fstatus)" + fi + echo -e "$fol" + done + fi +} + + +# Interactive setup +quickstart() { + echo -e "---------- $_name quickstart ----------\n" + # Check for existing conf + if [ -f "$_def_conf" ]; then + read -p "A config file already exists. Overwrite it? [Y/n] " ow_config + while [[ ! "$ow_config" = "y" && ! "$ow_config" = "n" ]]; do + read -p "Please answer 'y' or 'n': " ow_config + done + if [ "$ow_config" = "n" ]; then + mv $_def_conf $_def_conf-$(date +"%Y-%m-%d-%H%M"); + echo -e "Old config copied to $_def_conf-$(date +"%Y-%m-%d-%H%M")" + fi + fi + + read -p "What's your nick? [$(whoami)] " nick + read -p "Where do you want to store your tweets? (Enter full path) \ +[${_options[1]/* = /}] " twtfile + read -p "Where can others find your tweets? (File should end in .txt) \ +[${_options[2]/* = /}] " twturl + create_config "$nick" "$twtfile" "$twturl" + echo -e "Config created in $(dirname $_def_conf). Ready to tweet!" +} + + +# Add user to registry +api_add_user() { + local resp=`curlp "post" \ + "${registries[0]}users?url=$twturl&nickname=$nick"` + case $resp in + OK) echo -e "Your nick has been added to the registry. Hooray!";; + *) echo -e "Error: $resp";; + esac +} + + +# Search registry for a tag, tweet or user +api_search() { + if [ -n "$1" ] && [ -n "$2" ]; then + local src_data + case $1 in + keyword) src_data=`curlp "src" "${registries[0]}tweets?q=$2"`;; + tag) src_data=`curlp "src" "${registries[0]}tags/$2"`;; + user) src_data=`curlp "src" "${registries[0]}users?q=$2"`;; + *) echo "Invalid search option. Options: keyword, tag, user";; + esac + if [ -z "$src_data" ]; then + echo -e "No search results found."; exit 0 + else + output_twdata "search" "$src_data" + fi + else + echo -e "Usage: $_name search keyword|tag|user [keyword]" + fi +} + + +# Get timeline by type: user, mentions (registry), public (registry) +api_timeline() { + local ftype src_data + case $1 in + me) + ftype="timeline_me" + src_data=`cat $twtfile` + if [ -z "$src_data" ]; then + echo -e "Nothing to see yet. Try tweeting first?"; exit 0 + fi;; + mentions) + ftype="timeline" + src_data=`curlp "src" "${registries[0]}mentions?url=$twturl"` + if [ -z "$src_data" ]; then + echo -e "No mentions found."; exit 0 + fi;; + public) + ftype="timeline" + src_data=`curlp "src" "${registries[0]}tweets"` + if [ -z "$src_data" ]; then + echo -e "No tweets found."; exit 0 + fi;; + *) echo -n "Usage: $_name timeline me|mentions|public"; exit 1;; + esac + output_twdata "$ftype" "$src_data" +} + + +# View another user's timeline given a nick or source url +view() { + local src src_data + if [ -n "$1" ]; then + # if: check whether it's a source and attempt to resolve url + # elif: check for nick match + # else: exit with no match found + if [[ "$1" =~ "http://" || "$1" =~ "https://" ]]; then + src_data=`curlp "src" "$1"` + elif [ -z "$src_data" ]; then + src=`cat $_def_conf | grep "$1"` + if [ -n "$src" ]; then + src_data=`curlp "src" "${src/* = /}"` + fi + else + echo -e "No tweets found from nick/source."; exit 1 + fi + echo -e "Timeline of ${_fmt_bold}$1${_fmt_reset}:\n" + output_twdata "timeline_me" "$src_data" + else + echo -e "Usage: $_name view [source]" + fi +} + + +# Load tweet hook commands +tweet_hook() { + local hook msg + case "$1" in + pre) + hook="$pre_tweet_hook" + msg="Running pre-tweet hook ...";; + post) + hook="$post_tweet_hook" + msg="Running post-tweek hook ...";; + esac + hook=`echo -e "$hook" | sed "s|\"||g"` + if [ -n "$hook" ]; then + echo -e "$msg" + eval "$hook" || exit 1 + echo -e "Done." + fi +} + + +# Add a tweet to user timeline +tweet() { + if [ ! -f "$twtfile" ]; then touch "$twtfile"; fi + # Launch external editor to compose + local buffer=$(dirname $_def_conf)/tweet.tmp + $editor $buffer + + # Check for empty tweet + if [ ! -f $buffer ] || [ -z "$(cat $buffer)" ]; then exit 0; fi + + # Alert user if character limit warning is enabled and limit exceeded + local twt="$(cat $buffer)" + while [ ${#twt} -gt $character_warning ] && \ + [ "$character_warning_on" = "True" ]; do + read -p "Your tweet is ${#twt} chars long (limit $character_warning \ +chars). What would you like to do? [e]dit / [i]gnore : " resp_limit + while [[ ! "$resp_limit" = "e" && ! "$resp_limit" = "i" ]]; do + read -p "Please answer 'e' (edit) or 'i' (ignore) : " \ + resp_limit + done + case $resp_limit in + e) $editor $buffer;; + i) break;; + esac + done + + # Save tweet + tweet_hook "pre" + echo -e "$(date +%FT%T%:z)\t$(cat $buffer)" >> $twtfile + rm -rf $buffer + echo -e "Tweet added. Cheers!" + tweet_hook "post" +} + + +# Add new tweet (inline variant) +# Limitation: like most bash shell scripts, using special characters at +# the prompt without manual escaping will cause bash to throw an error. +# Retained for convenience. +quicktweet() { + if [ ! -f "$twtfile" ]; then touch "$twtfile"; fi + if [ -z "$1" ]; then + echo -e "No empty tweets, please. Try again?"; exit 0 + fi + + # Alert user if character limit warning is enabled and limit exceeded + local twt="$1" + while [ ${#twt} -gt $character_warning ] && \ + [ "$character_warning_on" = "True" ]; do + read -p "Your tweet is ${#twt} chars long (limit $character_warning \ +chars). What would you like to do? [e]dit / [i]gnore : " resp_limit + while [[ ! "$resp_limit" = "e" && ! "$resp_limit" = "i" ]]; do + read -p "Please answer 'e' (edit) or 'i' (ignore) : " \ + resp_limit + done + case $resp_limit in + e) read -e -i "$twt" -p "Edit tweet: " twt;; + i) break;; + esac + done + + # Save tweet + tweet_hook "pre" + echo -e "`date +%FT%T%:z`\t$twt" >> $twtfile + echo -e "Tweet added. Cheers!" + tweet_hook "post" +} + + +# Unfollow a source +unfollow() { + local is_following=$(echo $following | grep "$1") + if [ -n "$1" ] && [ -n "$is_following" ]; then + sed -i "s|.*$1|__unfollowdelete|" $_def_conf + printf "$(cat $_def_conf | sed "/__unfollowdelete/d")" > $_def_conf + echo -e "You have unfollowed \ +${_fmt_bold}${is_following/ = */}${_fmt_reset} @ $1." + elif [ -z "$is_following" ]; then + echo -e "You're not currently following this source." + else + echo -e "Please specify a source url." + fi +} + + +# Command switch +case "$1" in + config) edit_config $2 $3;; + follow) load_config; follow $2 $3;; + following) load_config; following;; + qt) load_config; quicktweet "$2";; + quickstart) quickstart;; + register) load_config; api_add_user;; + search) load_config; api_search $2 $3;; + timeline) load_config; api_timeline $2;; + tweet) load_config; tweet;; + unfollow) load_config; unfollow $2;; + view) load_config; view $2 $3;; + --version|version) echo -e "$_name v. $_version";; + *) echo -e "$_name — $_desc\n\n\ +Usage: $_name [command] [args]\n\n\ +Commands:\n\ + config\t\tEdit the config file\n\ + follow\t\tAdd a new source to follow\n\ + following\t\tList sources you are following\n\ + qt\t\t\tQuickly add a new tweet\n\ + quickstart\t\tSet up $_name (run this first!)\n\ + register\t\tAdd your nick to registry (optional for discoverability)\n\ + search\t\tSearch registry by keyword, tag or user\n\ + timeline\t\tView your timeline, mentions or public timeline\n\ + tweet\t\tAdd a new tweet from a preferred text editor\n\ + unfollow\t\tRemove a source from your following list\n\ + view\t\tView a source by url\n\ + version\t\tShow the version\n\ + help\t\tShow this help message\n\n\ +Examples:\n\ + $_name config nick MyNick\t\t\tChange your nick to \"MyNick\"\n\ + $_name config edit\t\t\t\tEdit config file in an editor (default: vi)\n\ + $_name follow Foo https://foo.tld/twtxt.txt\tFollow user \"Foo\"\n\ + $_name search tag blog\t\t\tSearch registry for tweets with \"blog\" tag\n\ + $_name timeline me\t\t\t\tView your timeline\n\ + $_name qt \"Hello world!\"\t\t\tTweet a message\n\ + $_name unfollow https://foo.tld/twtxt.txt\tRemove url from followings\n\ + $_name view Foo\t\t\t\tView user Foo's twtxt if already on followings\n\ + $_name view https://foo.tld/twtxt.txt\tView source url\n\ +";; +esac