commit 802412ba224534f02eb017c5013edf0cf44c6f3c Author: mio Date: Fri Apr 21 02:31:52 2023 +0000 Initial commit 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 0000000..c06373c Binary files /dev/null and b/thirdparty/our/coffee differ 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