The response has been limited to 50k tokens of the smallest files in the repo. You can remove this limitation by removing the max tokens filter.
├── .gitignore
├── Dockerfile
├── LICENSE.txt
├── MANIFEST.in
├── README.rst
├── docs
    ├── Makefile
    ├── conf.py
    ├── index.rst
    └── make.bat
├── rainbowstream
    ├── __init__.py
    ├── c_image.py
    ├── colors.py
    ├── colorset
    │   ├── base16.json
    │   ├── config
    │   ├── larapaste.json
    │   ├── monokai.json
    │   ├── solarized.json
    │   └── tomorrow_night.json
    ├── config.py
    ├── draw.py
    ├── emoji.py
    ├── image.c
    ├── interactive.py
    ├── pure_image.py
    ├── py3patch.py
    ├── rainbow.py
    └── util.py
├── release.sh
├── screenshot
    ├── RainbowStreamAll.png
    ├── rs.gif
    └── themes
    │   ├── Default.png
    │   ├── Monokai.png
    │   ├── Solarized.png
    │   ├── TomorrowNight.png
    │   └── larapaste.png
├── setup.CFG
├── setup.py
└── theme.md


/.gitignore:
--------------------------------------------------------------------------------
 1 | # Byte-compiled / optimized / DLL files
 2 | __pycache__/
 3 | *.py[cod]
 4 | .DS_Store
 5 | 
 6 | # C extensions
 7 | *.so
 8 | 
 9 | # Pyenv
10 | .python-version
11 | 
12 | # Distribution / packaging
13 | .Python
14 | env/
15 | bin/
16 | build/
17 | _build/
18 | develop-eggs/
19 | dist/
20 | eggs/
21 | lib/
22 | lib64/
23 | parts/
24 | sdist/
25 | var/
26 | *.egg-info/
27 | .installed.cfg
28 | *.egg
29 | 
30 | # Installer logs
31 | pip-log.txt
32 | pip-delete-this-directory.txt
33 | 
34 | # Unit test / coverage reports
35 | htmlcov/
36 | .tox/
37 | .coverage
38 | .cache
39 | nosetests.xml
40 | coverage.xml
41 | 
42 | # Translations
43 | *.mo
44 | 
45 | # Mr Developer
46 | .mr.developer.cfg
47 | .project
48 | .pydevproject
49 | 
50 | # Rope
51 | .ropeproject
52 | 
53 | # Django stuff:
54 | *.log
55 | *.pot
56 | 
57 | # Sphinx documentation
58 | docs/_build/
59 | 
60 | # DB files
61 | *.db
62 | 
63 | # Editor
64 | *sublime*
65 | .tag*
66 | *.swp*
67 | *.swo*
68 | 
69 | # Virtualenv
70 | /venv*
71 | 
72 | # History completer
73 | completer.hist
74 | 
75 | # Consumer
76 | rainbowstream/consumer.py
77 | 


--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:2.7.9
2 | 
3 | RUN pip install rainbowstream
4 | CMD rainbowstream
5 | 


--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
 1 | Copyright (c) 2014 Vu Nhat Minh
 2 | 
 3 | Permission is hereby granted, free of charge, to any person obtaining a copy
 4 | of this software and associated documentation files (the "Software"), to deal
 5 | in the Software without restriction, including without limitation the rights
 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 7 | copies of the Software, and to permit persons to whom the Software is
 8 | furnished to do so, subject to the following conditions:
 9 | 
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 | 
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 | 


--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include README.rst LICENSE.txt
2 | include rainbowstream/image.c
3 | recursive-include rainbowstream/colorset *
4 | 


--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
  1 | Rainbow Stream
  2 | --------------
  3 | 
  4 | .. image:: http://img.shields.io/pypi/l/rainbowstream.svg?style=flat-square
  5 |    :target: https://github.com/DTVD/rainbowstream/blob/master/LICENSE.txt
  6 | 
  7 | .. image:: http://img.shields.io/pypi/v/rainbowstream.svg?style=flat-square
  8 |    :target: https://pypi.python.org/pypi/rainbowstream
  9 | 
 10 | Terminal-based full-fledged Twitter client, built upon `Python Twitter Tools`.
 11 | 
 12 | Showcase
 13 | --------
 14 | 
 15 | .. figure:: https://raw.githubusercontent.com/DTVD/rainbowstream/master/screenshot/rs.gif
 16 |    :alt: gif
 17 | 
 18 | Installation
 19 | ------------
 20 | 
 21 | Direct installation
 22 | ^^^^^^^^^^^^^^^^^^^
 23 | 
 24 | .. code:: bash
 25 | 
 26 |     sudo pip3 install rainbowstream
 27 | 
 28 | Virtualenv (Recommended)
 29 | ^^^^^^^^^^^^^^^^^^^^^^^^
 30 | 
 31 | .. code:: bash
 32 | 
 33 |     virtualenv -p /usr/bin/python3 venv
 34 |     source venv/bin/activate
 35 |     pip install rainbowstream
 36 | 
 37 | Installation Troubleshooting
 38 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 39 | If you run into dependency issues, you may want to install additional libraries
 40 | 
 41 | Debian-based distros:
 42 | 
 43 | .. code:: bash
 44 | 
 45 |     sudo apt-get install python-dev libjpeg-dev libfreetype6 libfreetype6-dev zlib1g-dev
 46 | 
 47 | CentOS:
 48 | 
 49 | .. code:: bash
 50 | 
 51 |     sudo yum install python-devel libjpeg-devel
 52 | 
 53 | Mac OSX 
 54 | Mac has a `clang unknown argument`_
 55 | problem with the ``Pillow`` package—a dependency of this
 56 | app.  Please see the workaround in `Issue #10`_
 57 | 
 58 | .. code:: bash
 59 | 
 60 |     export ARCHFLAGS=-Wno-error=unused-command-line-argument-hard-error-in-future
 61 | 
 62 | If you still experience issues:
 63 | 
 64 | -  ``sudo pip uninstall rainbowstream``
 65 | -  Use the *virtualenv installation*
 66 | -  `Create an issue`_ and provide:
 67 |     - Your OS
 68 |     - Your Python version
 69 | 
 70 | Usage
 71 | -----
 72 | 
 73 | The Stream
 74 | ^^^^^^^^^^
 75 | 
 76 | Simply run ``rainbowstream`` to start the application, or enjoy its ASCII images with ``rainbowstream -iot`` or set ``IMAGE_ON_TERM`` to ``True`` in your config.
 77 | 
 78 | If your terminal supports 24-bit colors, run ``rainbowstream -p24`` instead to utilize 24 bit ASCII images.
 79 | 
 80 | If your terminal supports sixel, ie. wezterm or MLTerm, change the ``IMAGE_ON_TERM`` config to ``sixel`` and enjoy high-quality images.
 81 | 
 82 | You might want to change ``IMAGE_SHIFT`` to set the image's margin (relative to your terminal's
 83 | width), and ``IMAGE_MAX_HEIGHT`` to control the max height of every image (see
 84 | `Config Management`_).
 85 | 
 86 | You will be asked for Twitter authorization the first time you run Rainbow
 87 | Stream.  Just click the "Authorize access" button, paste the PIN to the
 88 | terminal, and the application will start.
 89 | 
 90 | You might want to use Rainbow Stream with an **HTTP/SOCKS proxy**.  Proxy
 91 | settings are specified as follows:
 92 | 
 93 | .. code:: bash
 94 | 
 95 |     rainbowstream --proxy-host localhost --proxy-port 1337 --proxy-type HTTP
 96 |     # or the short form:
 97 |     rainbowstream -ph localhost -pp 1337 -pt HTTP
 98 | 
 99 | Both ``--proxy-port`` and ``--proxy-type`` are optional.  The default proxy port
100 | is ``8080`` and the default proxy type is ``SOCKS5``.
101 | 
102 | Interactive Mode
103 | ^^^^^^^^^^^^^^^^
104 | 
105 | While your stream is continued, you are also ready to tweet, search,
106 | reply, retweet, etc. directly from your console.  Simply type ``h`` and hit the
107 | Enter key to see the help.
108 | 
109 | Input is in interactive mode.  It means that you can use the arrow keys to move
110 | up and down through the history, tab-autocomplete, or double-tab to view
111 | available suggestions.  Input history from the previous run is also available.
112 | 
113 | `Read the docs`_ for available commands.
114 | 
115 | Theme Customization
116 | ^^^^^^^^^^^^^^^^^^^
117 | 
118 | Rainbow Stream is shipped with some default themes.  You can switch themes with
119 | the ``theme`` command.  You can also customize themes as you please.
120 | 
121 | Theme screenshots:
122 | 
123 | - Monokai
124 | 
125 | .. figure:: https://raw.githubusercontent.com/DTVD/rainbowstream/master/screenshot/themes/Monokai.png
126 |    :alt: monokai
127 | 
128 | - Solarized
129 | 
130 | .. figure:: https://raw.githubusercontent.com/DTVD/rainbowstream/master/screenshot/themes/Solarized.png
131 |    :alt: solarized
132 | 
133 | - Tomorrow Night
134 | 
135 | .. figure:: https://raw.githubusercontent.com/DTVD/rainbowstream/master/screenshot/themes/TomorrowNight.png
136 |    :alt: tomorrownight
137 | 
138 | - Larapaste
139 | 
140 | .. figure:: https://raw.githubusercontent.com/DTVD/rainbowstream/master/screenshot/themes/larapaste.png
141 |    :alt: larapaste
142 | 
143 | See `Theme Usage and Customization`_ for detailed information.
144 | 
145 | A Note about Twitter API Change
146 | -------------------------------
147 | 
148 | Since Twitter discontinued supporting Stream API, RainbowStream is now using a [Polling Strategy](https://github.com/orakaro/rainbowstream/issues/271) that utilizes the `home` command to poll for your tweets every 90 seconds. This `home` command is rate limited by 15 times per 15 minutes, so don't run it too frequently to leave space for the polling stream.
149 | 
150 | Bug and Feature Requests
151 | ------------------------
152 | 
153 | Found a bug or a feature request?  Please `create an issue`_ or contact me at
154 | `@orakaro`_.
155 | 
156 | Development
157 | -----------
158 | 
159 | If you want to build a runnable version yourself, follow these simple steps:
160 | 
161 | - `Create your Twitter Application`_
162 | -  Get your Twitter application’s API key and secret
163 | - `Create your own Pocket Application`_ (platform: Web)
164 | -  Get your Pocket application’s key
165 | -  Fork this repo and ``git clone`` it
166 | -  Create a ``consumer.py`` file in the `rainbowstream` directory containing:
167 | 
168 |    .. code:: python
169 | 
170 |        # Consumer information
171 |        CONSUMER_KEY = 'APIKey' # Your Twitter application's API key
172 |        CONSUMER_SECRET = 'APISecret' # Your Twitter application's API secret
173 |        PCKT_CONSUMER_KEY = 'PocketAPIKey' # Your Pocket application's API key
174 | 
175 | -  Use pip to install it locally
176 | 
177 |    .. code:: bash
178 | 
179 |        # cd to directory which contains setup.py (cloned directory)
180 |        virtualenv venv # Python3 users: use -p to specify python3
181 |        source venv/bin/activate
182 |        pip install -e .
183 |        which rainbowstream # /this-directory/venv/bin/rainbowstream
184 |        # Remove ~/.rainbow_oauth if it exists
185 |        rainbowstream # local version of rainbowstream
186 | 
187 | 
188 | Contributing
189 | ------------
190 | 
191 | I appreciate any help and support.  Feel free to `fork`_ and `create a pull
192 | request`_. 
193 | 
194 | License
195 | -------
196 | 
197 | Rainbow Stream is released under an MIT License.  See LICENSE.txt for details.
198 | 
199 | 
200 | .. _Python Twitter Tools: http://mike.verdone.ca/twitter/
201 | .. _Twitter API: https://dev.twitter.com/docs/api/1.1
202 | .. _Create an issue: https://github.com/DTVD/rainbowstream/issues/new
203 | .. _@orakaro: https://twitter.com/dtvd88
204 | .. _fork: https://github.com/DTVD/rainbowstream/fork
205 | .. _create a pull request: https://github.com/DTVD/rainbowstream/compare/
206 | .. _Read the docs: http://rainbowstream.readthedocs.org/en/latest/
207 | .. _config guide: https://github.com/DTVD/rainbowstream/blob/master/theme.md
208 | .. _Theme Usage and Customization: https://github.com/DTVD/rainbowstream/blob/master/theme.md
209 | .. _Create your Twitter Application: https://apps.twitter.com/app/new
210 | .. _Create your own Pocket Application: https://getpocket.com/developer/apps/new
211 | .. _Config Management: http://rainbowstream.readthedocs.org/en/latest/#config-explanation
212 | .. _clang unknown argument: http://kaspermunck.github.io/2014/03/fixing-clang-error/
213 | .. _Issue #10: https://github.com/DTVD/rainbowstream/issues/10
214 | 


--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
  1 | # Makefile for Sphinx documentation
  2 | #
  3 | 
  4 | # You can set these variables from the command line.
  5 | SPHINXOPTS    =
  6 | SPHINXBUILD   = sphinx-build
  7 | PAPER         =
  8 | BUILDDIR      = _build
  9 | 
 10 | # User-friendly check for sphinx-build
 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $?), 1)
 12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
 13 | endif
 14 | 
 15 | # Internal variables.
 16 | PAPEROPT_a4     = -D latex_paper_size=a4
 17 | PAPEROPT_letter = -D latex_paper_size=letter
 18 | ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
 19 | # the i18n builder cannot share the environment and doctrees with the others
 20 | I18NSPHINXOPTS  = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
 21 | 
 22 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
 23 | 
 24 | help:
 25 | 	@echo "Please use \`make <target>' where <target> is one of"
 26 | 	@echo "  html       to make standalone HTML files"
 27 | 	@echo "  dirhtml    to make HTML files named index.html in directories"
 28 | 	@echo "  singlehtml to make a single large HTML file"
 29 | 	@echo "  pickle     to make pickle files"
 30 | 	@echo "  json       to make JSON files"
 31 | 	@echo "  htmlhelp   to make HTML files and a HTML help project"
 32 | 	@echo "  qthelp     to make HTML files and a qthelp project"
 33 | 	@echo "  devhelp    to make HTML files and a Devhelp project"
 34 | 	@echo "  epub       to make an epub"
 35 | 	@echo "  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
 36 | 	@echo "  latexpdf   to make LaTeX files and run them through pdflatex"
 37 | 	@echo "  latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
 38 | 	@echo "  text       to make text files"
 39 | 	@echo "  man        to make manual pages"
 40 | 	@echo "  texinfo    to make Texinfo files"
 41 | 	@echo "  info       to make Texinfo files and run them through makeinfo"
 42 | 	@echo "  gettext    to make PO message catalogs"
 43 | 	@echo "  changes    to make an overview of all changed/added/deprecated items"
 44 | 	@echo "  xml        to make Docutils-native XML files"
 45 | 	@echo "  pseudoxml  to make pseudoxml-XML files for display purposes"
 46 | 	@echo "  linkcheck  to check all external links for integrity"
 47 | 	@echo "  doctest    to run all doctests embedded in the documentation (if enabled)"
 48 | 
 49 | clean:
 50 | 	rm -rf $(BUILDDIR)/*
 51 | 
 52 | html:
 53 | 	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
 54 | 	@echo
 55 | 	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
 56 | 
 57 | dirhtml:
 58 | 	$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
 59 | 	@echo
 60 | 	@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
 61 | 
 62 | singlehtml:
 63 | 	$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
 64 | 	@echo
 65 | 	@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
 66 | 
 67 | pickle:
 68 | 	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
 69 | 	@echo
 70 | 	@echo "Build finished; now you can process the pickle files."
 71 | 
 72 | json:
 73 | 	$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
 74 | 	@echo
 75 | 	@echo "Build finished; now you can process the JSON files."
 76 | 
 77 | htmlhelp:
 78 | 	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
 79 | 	@echo
 80 | 	@echo "Build finished; now you can run HTML Help Workshop with the" \
 81 | 	      ".hhp project file in $(BUILDDIR)/htmlhelp."
 82 | 
 83 | qthelp:
 84 | 	$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
 85 | 	@echo
 86 | 	@echo "Build finished; now you can run "qcollectiongenerator" with the" \
 87 | 	      ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
 88 | 	@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/RainbowStream.qhcp"
 89 | 	@echo "To view the help file:"
 90 | 	@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/RainbowStream.qhc"
 91 | 
 92 | devhelp:
 93 | 	$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
 94 | 	@echo
 95 | 	@echo "Build finished."
 96 | 	@echo "To view the help file:"
 97 | 	@echo "# mkdir -p $HOME/.local/share/devhelp/RainbowStream"
 98 | 	@echo "# ln -s $(BUILDDIR)/devhelp $HOME/.local/share/devhelp/RainbowStream"
 99 | 	@echo "# devhelp"
100 | 
101 | epub:
102 | 	$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
103 | 	@echo
104 | 	@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
105 | 
106 | latex:
107 | 	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
108 | 	@echo
109 | 	@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
110 | 	@echo "Run \`make' in that directory to run these through (pdf)latex" \
111 | 	      "(use \`make latexpdf' here to do that automatically)."
112 | 
113 | latexpdf:
114 | 	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
115 | 	@echo "Running LaTeX files through pdflatex..."
116 | 	$(MAKE) -C $(BUILDDIR)/latex all-pdf
117 | 	@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
118 | 
119 | latexpdfja:
120 | 	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
121 | 	@echo "Running LaTeX files through platex and dvipdfmx..."
122 | 	$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
123 | 	@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
124 | 
125 | text:
126 | 	$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
127 | 	@echo
128 | 	@echo "Build finished. The text files are in $(BUILDDIR)/text."
129 | 
130 | man:
131 | 	$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
132 | 	@echo
133 | 	@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
134 | 
135 | texinfo:
136 | 	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
137 | 	@echo
138 | 	@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
139 | 	@echo "Run \`make' in that directory to run these through makeinfo" \
140 | 	      "(use \`make info' here to do that automatically)."
141 | 
142 | info:
143 | 	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
144 | 	@echo "Running Texinfo files through makeinfo..."
145 | 	make -C $(BUILDDIR)/texinfo info
146 | 	@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
147 | 
148 | gettext:
149 | 	$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
150 | 	@echo
151 | 	@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
152 | 
153 | changes:
154 | 	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
155 | 	@echo
156 | 	@echo "The overview file is in $(BUILDDIR)/changes."
157 | 
158 | linkcheck:
159 | 	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
160 | 	@echo
161 | 	@echo "Link check complete; look for any errors in the above output " \
162 | 	      "or in $(BUILDDIR)/linkcheck/output.txt."
163 | 
164 | doctest:
165 | 	$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
166 | 	@echo "Testing of doctests in the sources finished, look at the " \
167 | 	      "results in $(BUILDDIR)/doctest/output.txt."
168 | 
169 | xml:
170 | 	$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
171 | 	@echo
172 | 	@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
173 | 
174 | pseudoxml:
175 | 	$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
176 | 	@echo
177 | 	@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
178 | 


--------------------------------------------------------------------------------
/docs/conf.py:
--------------------------------------------------------------------------------
  1 | # -*- coding: utf-8 -*-
  2 | #
  3 | # RainbowStream documentation build configuration file, created by
  4 | # sphinx-quickstart on Wed Jul 23 12:26:18 2014.
  5 | #
  6 | # This file is execfile()d with the current directory set to its
  7 | # containing dir.
  8 | #
  9 | # Note that not all possible configuration values are present in this
 10 | # autogenerated file.
 11 | #
 12 | # All configuration values have a default; values that are commented out
 13 | # serve to show the default.
 14 | 
 15 | # If extensions (or modules to document with autodoc) are in another directory,
 16 | # add these directories to sys.path here. If the directory is relative to the
 17 | # documentation root, use os.path.abspath to make it absolute, like shown here.
 18 | #sys.path.insert(0, os.path.abspath('.'))
 19 | 
 20 | # -- General configuration ------------------------------------------------
 21 | 
 22 | # If your documentation needs a minimal Sphinx version, state it here.
 23 | #needs_sphinx = '1.0'
 24 | 
 25 | # Add any Sphinx extension module names here, as strings. They can be
 26 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
 27 | # ones.
 28 | extensions = []
 29 | 
 30 | # Add any paths that contain templates here, relative to this directory.
 31 | templates_path = ['_templates']
 32 | 
 33 | # The suffix of source filenames.
 34 | source_suffix = '.rst'
 35 | 
 36 | # The encoding of source files.
 37 | #source_encoding = 'utf-8-sig'
 38 | 
 39 | # The master toctree document.
 40 | master_doc = 'index'
 41 | 
 42 | # General information about the project.
 43 | project = u'RainbowStream'
 44 | copyright = u'2014, Vu Nhat Minh'
 45 | 
 46 | # The version info for the project you're documenting, acts as replacement for
 47 | # |version| and |release|, also used in various other places throughout the
 48 | # built documents.
 49 | #
 50 | # The short X.Y version.
 51 | version = '1.6.0'
 52 | # The full version, including alpha/beta/rc tags.
 53 | release = '1.6.0'
 54 | 
 55 | # The language for content autogenerated by Sphinx. Refer to documentation
 56 | # for a list of supported languages.
 57 | #language = None
 58 | 
 59 | # There are two options for replacing |today|: either, you set today to some
 60 | # non-false value, then it is used:
 61 | #today = ''
 62 | # Else, today_fmt is used as the format for a strftime call.
 63 | #today_fmt = '%B %d, %Y'
 64 | 
 65 | # List of patterns, relative to source directory, that match files and
 66 | # directories to ignore when looking for source files.
 67 | exclude_patterns = ['_build']
 68 | 
 69 | # The reST default role (used for this markup: `text`) to use for all
 70 | # documents.
 71 | #default_role = None
 72 | 
 73 | # If true, '()' will be appended to :func: etc. cross-reference text.
 74 | #add_function_parentheses = True
 75 | 
 76 | # If true, the current module name will be prepended to all description
 77 | # unit titles (such as .. function::).
 78 | #add_module_names = True
 79 | 
 80 | # If true, sectionauthor and moduleauthor directives will be shown in the
 81 | # output. They are ignored by default.
 82 | #show_authors = False
 83 | 
 84 | # The name of the Pygments (syntax highlighting) style to use.
 85 | pygments_style = 'sphinx'
 86 | 
 87 | # A list of ignored prefixes for module index sorting.
 88 | #modindex_common_prefix = []
 89 | 
 90 | # If true, keep warnings as "system message" paragraphs in the built documents.
 91 | #keep_warnings = False
 92 | 
 93 | 
 94 | # -- Options for HTML output ----------------------------------------------
 95 | 
 96 | # The theme to use for HTML and HTML Help pages.  See the documentation for
 97 | # a list of builtin themes.
 98 | html_theme = 'nature'
 99 | 
100 | # Theme options are theme-specific and customize the look and feel of a theme
101 | # further.  For a list of options available for each theme, see the
102 | # documentation.
103 | #html_theme_options = {}
104 | 
105 | # Add any paths that contain custom themes here, relative to this directory.
106 | #html_theme_path = []
107 | 
108 | # The name for this set of Sphinx documents.  If None, it defaults to
109 | # "<project> v<release> documentation".
110 | #html_title = None
111 | 
112 | # A shorter title for the navigation bar.  Default is the same as html_title.
113 | #html_short_title = None
114 | 
115 | # The name of an image file (relative to this directory) to place at the top
116 | # of the sidebar.
117 | #html_logo = None
118 | 
119 | # The name of an image file (within the static path) to use as favicon of the
120 | # docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
121 | # pixels large.
122 | #html_favicon = None
123 | 
124 | # Add any paths that contain custom static files (such as style sheets) here,
125 | # relative to this directory. They are copied after the builtin static files,
126 | # so a file named "default.css" will overwrite the builtin "default.css".
127 | html_static_path = ['_static']
128 | 
129 | # Add any extra paths that contain custom files (such as robots.txt or
130 | # .htaccess) here, relative to this directory. These files are copied
131 | # directly to the root of the documentation.
132 | #html_extra_path = []
133 | 
134 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
135 | # using the given strftime format.
136 | #html_last_updated_fmt = '%b %d, %Y'
137 | 
138 | # If true, SmartyPants will be used to convert quotes and dashes to
139 | # typographically correct entities.
140 | #html_use_smartypants = True
141 | 
142 | # Custom sidebar templates, maps document names to template names.
143 | #html_sidebars = {}
144 | 
145 | # Additional templates that should be rendered to pages, maps page names to
146 | # template names.
147 | #html_additional_pages = {}
148 | 
149 | # If false, no module index is generated.
150 | #html_domain_indices = True
151 | 
152 | # If false, no index is generated.
153 | #html_use_index = True
154 | 
155 | # If true, the index is split into individual pages for each letter.
156 | #html_split_index = False
157 | 
158 | # If true, links to the reST sources are added to the pages.
159 | #html_show_sourcelink = True
160 | 
161 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
162 | #html_show_sphinx = True
163 | 
164 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
165 | #html_show_copyright = True
166 | 
167 | # If true, an OpenSearch description file will be output, and all pages will
168 | # contain a <link> tag referring to it.  The value of this option must be the
169 | # base URL from which the finished HTML is served.
170 | #html_use_opensearch = ''
171 | 
172 | # This is the file name suffix for HTML files (e.g. ".xhtml").
173 | #html_file_suffix = None
174 | 
175 | # Output file base name for HTML help builder.
176 | htmlhelp_basename = 'RainbowStreamdoc'
177 | 
178 | 
179 | # -- Options for LaTeX output ---------------------------------------------
180 | 
181 | latex_elements = {
182 | # The paper size ('letterpaper' or 'a4paper').
183 | #'papersize': 'letterpaper',
184 | 
185 | # The font size ('10pt', '11pt' or '12pt').
186 | #'pointsize': '10pt',
187 | 
188 | # Additional stuff for the LaTeX preamble.
189 | #'preamble': '',
190 | }
191 | 
192 | # Grouping the document tree into LaTeX files. List of tuples
193 | # (source start file, target name, title,
194 | #  author, documentclass [howto, manual, or own class]).
195 | latex_documents = [
196 |   ('index', 'RainbowStream.tex', u'RainbowStream Documentation',
197 |    u'Vu Nhat Minh', 'manual'),
198 | ]
199 | 
200 | # The name of an image file (relative to this directory) to place at the top of
201 | # the title page.
202 | #latex_logo = None
203 | 
204 | # For "manual" documents, if this is true, then toplevel headings are parts,
205 | # not chapters.
206 | #latex_use_parts = False
207 | 
208 | # If true, show page references after internal links.
209 | #latex_show_pagerefs = False
210 | 
211 | # If true, show URL addresses after external links.
212 | #latex_show_urls = False
213 | 
214 | # Documents to append as an appendix to all manuals.
215 | #latex_appendices = []
216 | 
217 | # If false, no module index is generated.
218 | #latex_domain_indices = True
219 | 
220 | 
221 | # -- Options for manual page output ---------------------------------------
222 | 
223 | # One entry per manual page. List of tuples
224 | # (source start file, name, description, authors, manual section).
225 | man_pages = [
226 |     ('index', 'rainbowstream', u'RainbowStream Documentation',
227 |      [u'Vu Nhat Minh'], 1)
228 | ]
229 | 
230 | # If true, show URL addresses after external links.
231 | #man_show_urls = False
232 | 
233 | 
234 | # -- Options for Texinfo output -------------------------------------------
235 | 
236 | # Grouping the document tree into Texinfo files. List of tuples
237 | # (source start file, target name, title, author,
238 | #  dir menu entry, description, category)
239 | texinfo_documents = [
240 |   ('index', 'RainbowStream', u'RainbowStream Documentation',
241 |    u'Vu Nhat Minh', 'RainbowStream', 'One line description of project.',
242 |    'Miscellaneous'),
243 | ]
244 | 
245 | # Documents to append as an appendix to all manuals.
246 | #texinfo_appendices = []
247 | 
248 | # If false, no module index is generated.
249 | #texinfo_domain_indices = True
250 | 
251 | # How to display URL addresses: 'footnote', 'no', or 'inline'.
252 | #texinfo_show_urls = 'footnote'
253 | 
254 | # If true, do not generate a @detailmenu in the "Top" node's menu.
255 | #texinfo_no_detailmenu = False
256 | 


--------------------------------------------------------------------------------
/docs/index.rst:
--------------------------------------------------------------------------------
  1 | Rainbow Stream
  2 | --------------
  3 | 
  4 | .. image:: http://img.shields.io/pypi/l/rainbowstream.svg?style=flat-square
  5 |    :target: https://github.com/DTVD/rainbowstream/blob/master/LICENSE.txt
  6 | 
  7 | .. image:: http://img.shields.io/pypi/v/rainbowstream.svg?style=flat-square
  8 |    :target: https://pypi.python.org/pypi/rainbowstream
  9 | 
 10 | Terminal-based Twitter Client. Realtime tweetstream, compose, search ,
 11 | favorite … and much more fun directly from terminal.
 12 | 
 13 | This package is built on the top of `Python Twitter Tool`_ and `Twitter API`_,
 14 | can run on Python 2.7.x and 3.x .
 15 | 
 16 | Home page : http://www.rainbowstream.org/
 17 | 
 18 | Source code : https://github.com/DTVD/rainbowstream
 19 | 
 20 | Install
 21 | -------
 22 | 
 23 | The quick way
 24 | ^^^^^^^^^^^^^
 25 | 
 26 | You will need Python and pip (2.7.x or 3.x).
 27 | 
 28 | .. code:: bash
 29 | 
 30 |     sudo pip install rainbowstream
 31 |     # Python 3 users: sudo pip3 install rainbowstream
 32 | 
 33 | The recommended way
 34 | ^^^^^^^^^^^^^^^^^^^
 35 | 
 36 | Use `virtualenv`_
 37 | 
 38 | .. code:: bash
 39 | 
 40 |     virtualenv venv
 41 |     # Python 3 users : use -p to specify your Python 3 localtion as below
 42 |     # virtualenv -p /usr/bin/python3 venv
 43 |     source venv/bin/activate
 44 |     pip install rainbowstream
 45 | 
 46 | Troubleshooting
 47 | ^^^^^^^^^^^^^^^
 48 | 
 49 | If you use Linux, you might need to install some packages if you haven't already.
 50 | For debian-based distros, these can be installed with
 51 | 
 52 | .. code:: bash
 53 | 
 54 |     sudo apt-get install python-dev libjpeg libjpeg-dev libfreetype6 libfreetype6-dev zlib1g-dev
 55 | 
 56 | Besides, Mac OSX Maverick with Xcode 5.1 has a well-known `clang unknown argument`_ problem with
 57 | the ``Pillow`` package installation - a dependency of this app.
 58 | If you are in this case, I recommend taking a look at `Issue #10`_ and let me know if this workaround doesn't work for you.
 59 | 
 60 | .. code:: bash
 61 | 
 62 |     export ARCHFLAGS=-Wno-error=unused-command-line-argument-hard-error-in-future
 63 | 
 64 | If installation in *the quick way* doesn't work:
 65 | 
 66 | -  ``sudo pip uninstall rainbowstream``
 67 | -  use the *virtualenv way* above
 68 | -  `create an issue`_ and provide:
 69 | 
 70 |   + Your OS
 71 |   + Your Python version
 72 | 
 73 | Usage
 74 | -----
 75 | 
 76 | The stream
 77 | ^^^^^^^^^^
 78 | 
 79 | Just type
 80 | 
 81 | .. code:: bash
 82 | 
 83 |     rainbowstream
 84 | 
 85 | and see your stream.
 86 | 
 87 | I shipped a feature which can display **tweet's images directly on terminal**.
 88 | You can try it with:
 89 | 
 90 | .. code:: bash
 91 | 
 92 |     rainbowstream -iot # Or rainbowstream --image-on-term
 93 | 
 94 | You also can change the config key ``IMAGE_ON_TERM`` to ``True`` inside the app
 95 | to enable above feature,
 96 | change ``IMAGE_SHIFT`` to set image's margin (relative to your terminal's width)
 97 | or ``IMAGE_MAX_HEIGHT`` to control max height of every image.
 98 | (see `config management`_ section).
 99 | 
100 | In the first time you will be asked for authorization of Rainbow Stream
101 | app at Twitter. Just click the “Authorize access” button and paste PIN
102 | number to the terminal, the rainbow will start.
103 | 
104 | You might want to use rainbowstream via an **HTTP/SOCKS proxy**. Proxy settings are
105 | provided as follows:
106 | 
107 | .. code:: bash
108 | 
109 |     rainbowstream --proxy-host localhost --proxy-port 1337 --proxy-type HTTP
110 |     # or using the short form:
111 |     rainbowstream -ph localhost -pp 1337 -pt HTTP
112 | 
113 | Both ``--proxy-port`` and ``--proxy-type`` can be omitted. In this case default
114 | proxy port ``8080`` and default proxy type ``SOCKS5`` are used.
115 | 
116 | If you would like to specify an alternate location for storing the OAuth credential files for both twitter and pocket you can do so with the ``--twitter-auth`` and ``--pocket-auth`` settings. This is also useful if you wish to have multiple accounts for rainbowstream. Specify a location as follows:
117 | 
118 | .. code:: bash
119 | 
120 |     rainbowstream --twitter-auth /path/to/twitter_oauth
121 |     # or using the short form:
122 |     rainbowstream -ta /path/to/twitter_oauth
123 | 
124 | If the oauth file doesn't exist at the location you specified then a new one will be created and you'll be required to autenticate with Twitter again.
125 | 
126 | The interactive mode
127 | ^^^^^^^^^^^^^^^^^^^^
128 | 
129 | While your personal stream is continued, you are also ready to tweet,
130 | search, reply, retweet… directly from console. Simply type “h” and hit
131 | the Enter key to see the help.
132 | 
133 | Input is in interactive mode. It means that you can use arrow key to
134 | move up and down history, tab-autocomplete or 2 tab to view available
135 | suggestion. Input history from previous run is available as well.
136 | 
137 | Here is full list of supported command:
138 | 
139 | **Explore Commands**
140 | 
141 | -  ``trend`` will show global trending topics. ``trend US`` will show trends in United States while ``trend JP Tokyo`` will show trends in Tokyo/Japan.
142 | 
143 | -  ``home`` will show your timeline. ``home 10`` will print exactly 10 tweets.
144 | 
145 | -  ``me`` will show your latest tweets. ``me 2`` will show your last 2 tweets.
146 | 
147 | -  ``notification`` will show your notification from the time you started RainbowStream.
148 | 
149 | -  ``mentions`` will show mentions timeline. ``mentions 7`` will show 7 mention tweets.
150 | 
151 | -  ``whois @dtvd88`` will show profile of @dtvd88.
152 | 
153 | -  ``view @mdo`` will show @mdo ’s timeline. ``view @dmo 9`` will print exactly 9 tweets.
154 | 
155 | -  ``s noah`` will search the word *‘noah’*. Result will come back with highlight. Search can be performed with or without hashtag.
156 | 
157 | **Tweet Commands**
158 | 
159 | -  ``t the rainbow is god's promise to noah`` will tweet exactly *‘the rainbow is god’s promise to noah’*.
160 | 
161 | -  ``rt 12`` will retweet the tweet with *[id=12]*. You can see id of each tweet beside the time.
162 | 
163 | -  ``quote 12`` will quote the tweet with *[id=12]*. If no extra text is added, the quote will be cancelled.
164 | 
165 | -  ``allrt 12 20`` will list 20 newest retweets of the tweet with *[id=12]*. If the number of retweets is not specified, 5 newest retweets will be listed instead.
166 | 
167 | -  ``conversation 12`` will show the chain of replies prior to the tweet with *[id=12]*.
168 | 
169 | -  ``rep 12 Really`` will reply *‘Really’* to the owner of the tweet with *[id=12]*.
170 | 
171 | -  ``repall 12 Really`` will reply *‘Really’* to all people in the tweet with *[id=12]*.
172 | 
173 | -  ``fav 12`` will favorite the tweet with *[id=12]*.
174 | 
175 | -  ``ufav 12`` will unfavorite tweet with *[id=12]*.
176 | 
177 | -  ``share 12`` will copy link to tweet with *[id=12]* to your clipboard if you are on a Mac, or display it directly if you are on Linux.
178 | 
179 | -  ``del 12`` will delete tweet with *[id=12]*.
180 | 
181 | -  ``show image 12`` will show the image in tweet with *[id=12]* in your OS’s image viewer.
182 | 
183 | -  ``open 12`` will open url in tweet with *[id=12]* in your OS’s default browser.
184 | 
185 | -  ``pt 12`` will add tweet with *[id=12]* in your Pocket list.
186 | 
187 | **Direct Messages Commands**
188 | 
189 | -  ``inbox`` will show inbox messages. ``inbox 7`` will show newest 7 messages.
190 | 
191 | -  ``thread 2`` will show full thread with [id=2].
192 | 
193 | -  ``mes @dtvd88 hi`` will send a ``hi`` message to @dtvd88.
194 | 
195 | -  ``trash 5`` will remove message with *[message\_id=5]*
196 | 
197 | **Friends and followers Commands**
198 | 
199 | -  ``ls fl`` will list all your followers (people who are following you).
200 | 
201 | -  ``ls fr`` will list all your friends (people who you are following).
202 | 
203 | -  ``fl @dtvd88`` will follow @dtvd88.
204 | 
205 | -  ``ufl @dtvd88`` will unfollow @dtvd88.
206 | 
207 | -  ``mute @dtvd88`` will mute @dtvd88.
208 | 
209 | -  ``unmute @dtvd88`` will unmute @dtvd88.
210 | 
211 | -  ``muting`` will list muting users.
212 | 
213 | -  ``block @dtvd88`` will block @dtvd88.
214 | 
215 | -  ``unblock @dtvd88`` will unblock @dtvd88.
216 | 
217 | -  ``report @dtvd88`` will report @dtvd88 as a spam account.
218 | 
219 | **Twitter list**
220 | 
221 | -  ``list`` will show all lists you are belong to.
222 | 
223 | -  ``list home`` will show timeline of list. You will be asked for list's name.
224 | 
225 | -  ``list all_mem`` will show list's all members.
226 | 
227 | -  ``list all_sub`` will show list's all subscribers.
228 | 
229 | -  ``list add`` will add specific person to a list owned by you.
230 | 
231 | -  ``list rm`` will remove specific person from a list owned by you.
232 | 
233 | -  ``list sub`` will subscribe you to a specific list.
234 | 
235 | -  ``list unsub`` will unsubscribe you from a specific list.
236 | 
237 | -  ``list own`` will show all list owned by you.
238 | 
239 | -  ``list new`` will create a new list.
240 | 
241 | -  ``list update`` will update a list owned by you.
242 | 
243 | -  ``list del`` will delete a list owned by you.
244 | 
245 | **Switching Stream Commands**
246 | 
247 | -  ``switch public #AKB48`` will switch current stream to public stream and track keyword ``AKB48``
248 | 
249 | -  ``switch public #AKB48 -f`` will do exactly as above but will ask you to provide 2 list:
250 | 
251 |    ``Only nicks`` decide what nicks will be include only.
252 | 
253 |    ``Ignore nicks``\ decide what nicks will be exclude.
254 | 
255 | -  ``switch mine`` will switch current stream to personal stream. ``-f`` will work as well.
256 | 
257 | -  ``switch list`` will switch to a Twitter list's stream. You will be asked for list name.
258 | 
259 | **Smart shell**
260 | 
261 | - Put anything to terminal, the app will try to eval and display result as a python interactive shell.
262 | 
263 |   + ``142857*2`` or ``101**3`` like a calculator.
264 |   + Even ``cal`` will show the calendar for current month.
265 |   + Put ``order_rainbow('anything')`` or ``random_rainbow('wahahaha')`` will make more fun :)
266 | 
267 | **Config Management**
268 | 
269 | -  ``theme`` will list available themes.
270 | 
271 |   + ``theme monokai`` will apply *monokai* theme immediately.
272 |   + Changed theme will be remember as the next time's default theme.
273 | 
274 | -  ``config`` will list all config key.
275 | 
276 |   + ``config ASCII_ART`` will output current value of *ASCII_ART* config key.
277 |   + ``config TREND_MAX default`` will output default value of *TREND_MAX* config key.
278 |   + ``config CUSTOM_CONFIG drop`` will drop *CUSTOM_CONFIG* config key.
279 |   + ``config IMAGE_ON_TERM = true`` will set value of *IMAGE_ON_TERM* config key to *True*.
280 | 
281 | **Screening Commands**
282 | 
283 | -  ``h`` will show the help.
284 | 
285 | -  ``p`` will pause the stream.
286 | 
287 | -  ``r`` will unpause the stream.
288 | 
289 | -  ``c`` will clear the screen.
290 | 
291 | -  ``v`` will show version info.
292 | 
293 | -  ``q`` will quit.
294 | 
295 | Theme customization
296 | ^^^^^^^^^^^^^^^^^^^
297 | 
298 | Rainbow Stream is shipped with some default themes.
299 | You can either change theme by ``theme`` command or create your favorite one.
300 | 
301 | Theme’s screenshot:
302 | 
303 | - Monokai
304 | 
305 | .. figure:: https://raw.githubusercontent.com/DTVD/rainbowstream/master/screenshot/themes/Monokai.png
306 |    :alt: monokai
307 | 
308 | - Solarized
309 | 
310 | .. figure:: https://raw.githubusercontent.com/DTVD/rainbowstream/master/screenshot/themes/Solarized.png
311 |    :alt: solarized
312 | 
313 | - Tomorrow Night
314 | 
315 | .. figure:: https://raw.githubusercontent.com/DTVD/rainbowstream/master/screenshot/themes/TomorrowNight.png
316 |    :alt: tomorrownight
317 | 
318 | - Larapaste
319 | 
320 | .. figure:: https://raw.githubusercontent.com/DTVD/rainbowstream/master/screenshot/themes/larapaste.png
321 |    :alt: larapaste
322 | 
323 | For detaile information, see `theme usage and customization`_.
324 | 
325 | Config explanation
326 | ^^^^^^^^^^^^^^^^^^
327 | 
328 | Rainbow Stream has a custom config file located at ``~/.rainbow_config.json`` which will be loaded **after** its `default config`_. You are free to change anything on your custom config. If you mess up the JSON format the app will still work but you'll need to overwrite your custom config with the `default config`_ to solve format problems.
329 | 
330 | If you would like to specify a different location for your custom config you can set the ``RAINBOW_CONFIG`` environment variable to the location of your custom config.
331 | 
332 | You also can view or set a new value of every config key with the ``config`` command (See **Interactive mode** section above).
333 | 
334 | -  ``POLLING_TIME``: Time in seconds between each automatic poll. Most Twitter accounts have a limit of 15 requests each 15 minutes. If in doubt, set it to 90 or more.
335 | 
336 | -  ``HEARTBEAT_TIMEOUT``: after this timeout (count by minutes), the stream will automatically hangup.
337 | 
338 | -  ``IMAGE_ON_TERM``: display tweet's image directly on terminal.
339 | 
340 | -  ``IMAGE_RESIZE_TO_FIT``: display tweet's image fit inside terminal view (width and height).
341 | 
342 | -  ``THEME``: current theme.
343 | 
344 | -  ``ASCII_ART``: display your twitter name by ascii art at stream begin or not.
345 | 
346 | -  ``HIDE_PROMPT``: hide prompt after receiving a tweet or not.
347 | 
348 | -  ``PREFIX``: display formated string of prompt.
349 | 
350 |     + ``#me``: Your username with '@', only available in personal stream.
351 |     + ``#place``: List name, only available in list stream.
352 |     + ``#owner``: Owner of list name, only available in list stream.
353 |     + ``#keyword``: Keyword, only available on public stream.
354 | 
355 | -  ``SEARCH_TYPE``: search type in 'search' command ('mixed','recent','popular').
356 | 
357 | -  ``SEARCH_MAX_RECORD``: max tweets can display on 'search' command.
358 | 
359 | -  ``HOME_TWEET_NUM``: default tweets to display on 'home' command.
360 | 
361 | -  ``RETWEETS_SHOW_NUM``: default tweets to display on 'allrt' command.
362 | 
363 | -  ``CONVERSATION_MAX``: max tweet in a 'conversation' thread.
364 | 
365 | -  ``QUOTE_FORMAT``: format when quote a tweet
366 | 
367 |     + ``#comment``: Your own comment about the tweet
368 |     + ``#owner``: owner's username *without* '@'
369 |     + ``#tweet``: original tweet
370 |     + ``#tid``: the tweet id on Twitter
371 | 
372 | -  ``THREAD_META_LEFT``: format for meta information of messages from partner which is display in the left of screen.
373 | 
374 | -  ``THREAD_META_RIGHT``: format for meta information of messages from you which is display in the right of screen.
375 | 
376 | -  ``THREAD_MIN_WIDTH``: minimum width of a message frame.
377 | 
378 | -  ``NOTIFY_FORMAT``: format of a notification.
379 | 
380 | -  ``MESSAGES_DISPLAY``: default messages to display on 'inbox' or 'sent' command.
381 | 
382 | -  ``TREND_MAX``: default trends to display on 'trend' command.
383 | 
384 | -  ``LIST_MAX``: default tweets to display on 'list home' command.
385 | 
386 | -  ``ONLY_LIST``: filter list on 'switch' command. Eg: ["@fat","mdo"]
387 | 
388 | -  ``IGNORE_LIST``: ignore list on 'switch' command. Eg: ["@fat"]
389 | 
390 | -  ``HISTORY_FILENAME``: name of file which stores input history.
391 | 
392 | -  ``IMAGE_SHIFT``: left and right margin of image in '-iot'/'--image-on-term' mode.
393 | 
394 | -  ``IMAGE_MAX_HEIGHT``: max height of image in '-iot'/'--image-on-term' mode.
395 | 
396 | -  ``STREAM_DELAY``: seconds to wait before displaying another tweet, will drop all tweets while waiting. This value can be used to slow down the stream.
397 | 
398 | -  ``USER_DOMAIN``: user URL of Twitter Streaming API.
399 | 
400 | -  ``PUBLIC_DOMAIN``: public URL of Twitter Streaming API.
401 | 
402 | -  ``SITE_DOMAIN``: site URL of Twitter Streaming API.
403 | 
404 | -  ``FORMAT``: display format for tweet and message.
405 | 
406 |   + ``CLOCK_FORMAT``: time format, see `Python's strftime format`_.
407 |   + ``DISPLAY``: decide how tweet will be printed.
408 | 
409 |     + ``#name``: Twitter's name
410 |     + ``#nick``: Twitter's screen name
411 |     + ``#clock``: Datetime
412 |     + ``#rt_count``: retweets count
413 |     + ``#fa_count``: favorites count
414 |     + ``#id``: ID
415 |     + ``#fav``: favorited symbol
416 |     + ``#fav``: favorited symbol
417 |     + ``#tweet``: Tweet's content
418 |     + ``#sender_name``: Message's sender name
419 |     + ``#sender_nick``: Message's sender screen name
420 |     + ``#to``: '>>>' symbol
421 |     + ``#recipient_name``: Message's recipient name
422 |     + ``#recipient_nick``: Message's recipient screen name
423 | 
424 | - ``POCKET_SUPPORT`` : enable Pocket support.
425 | 
426 | In every format, you can use unicode characters like ``\u2665``.
427 | Mac users also can use emoji characters as well (Ex: ``::zap::``).
428 | See `Emoji cheatsheet`_ for details.
429 | 
430 | Development
431 | -----------
432 | 
433 | If you want to build a runnable version yourself, follow these simple
434 | steps
435 | 
436 | -  `Create your own Twitter Application`_
437 | -  Get your Twitter application’s API key and secret
438 | -  Fork github's repo and clone in your system.
439 | -  Create a file ``consumer.py`` in `rainbowstream`_ folder with
440 |    following content
441 | 
442 |    .. code:: python
443 | 
444 |        # Consumer information
445 |        CONSUMER_KEY = 'APIKey' # Your Twitter application's API key
446 |        CONSUMER_SECRET = 'APISecret' # Your Twitter application's API secret
447 |        PCKT_CONSUMER_KEY = 'PocketAPIKey' # Your Pocket application's API key
448 | 
449 | -  Use pip to install in local
450 | 
451 |    .. code:: bash
452 | 
453 |        # cd to directory which contains setup.py (cloned directory)
454 |        virtualenv venv # Python3 users: use -p to specify python3
455 |        source venv/bin/activate
456 |        pip install -e .
457 |        which rainbowstream # /this-directory/venv/bin/rainbowstream
458 |        # Remove ~/.rainbow_oauth if exists
459 |        rainbowstream # local version of rainbowstream
460 | 
461 | .. _Python Twitter Tool: http://mike.verdone.ca/twitter/
462 | .. _Twitter API: https://dev.twitter.com/docs/api/1.1
463 | .. _Create your own Twitter Application: https://apps.twitter.com/app/new
464 | .. _Create your own Pocket Application: https://getpocket.com/developer/apps/new
465 | .. _rainbowstream: https://github.com/DTVD/rainbowstream/tree/master/rainbowstream
466 | .. _Python Twitter Tool: http://mike.verdone.ca/twitter/
467 | .. _Twitter API: https://dev.twitter.com/docs/api/1.1
468 | .. _theme usage and customization: https://github.com/DTVD/rainbowstream/blob/master/theme.md
469 | .. _virtualenv: http://docs.python-guide.org/en/latest/dev/virtualenvs/
470 | .. _config management: http://rainbowstream.readthedocs.org/en/latest/#config-explanation
471 | .. _Python's strftime format: https://docs.python.org/2/library/time.html#time.strftime
472 | .. _clang unknown argument: http://kaspermunck.github.io/2014/03/fixing-clang-error/
473 | .. _Issue #10: https://github.com/DTVD/rainbowstream/issues/10
474 | .. _default config: https://github.com/DTVD/rainbowstream/blob/master/rainbowstream/colorset/config
475 | .. _Emoji cheatsheet: http://www.emoji-cheat-sheet.com/
476 | 


--------------------------------------------------------------------------------
/docs/make.bat:
--------------------------------------------------------------------------------
  1 | @ECHO OFF
  2 | 
  3 | REM Command file for Sphinx documentation
  4 | 
  5 | if "%SPHINXBUILD%" == "" (
  6 | 	set SPHINXBUILD=sphinx-build
  7 | )
  8 | set BUILDDIR=_build
  9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
 10 | set I18NSPHINXOPTS=%SPHINXOPTS% .
 11 | if NOT "%PAPER%" == "" (
 12 | 	set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
 13 | 	set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
 14 | )
 15 | 
 16 | if "%1" == "" goto help
 17 | 
 18 | if "%1" == "help" (
 19 | 	:help
 20 | 	echo.Please use `make ^<target^>` where ^<target^> is one of
 21 | 	echo.  html       to make standalone HTML files
 22 | 	echo.  dirhtml    to make HTML files named index.html in directories
 23 | 	echo.  singlehtml to make a single large HTML file
 24 | 	echo.  pickle     to make pickle files
 25 | 	echo.  json       to make JSON files
 26 | 	echo.  htmlhelp   to make HTML files and a HTML help project
 27 | 	echo.  qthelp     to make HTML files and a qthelp project
 28 | 	echo.  devhelp    to make HTML files and a Devhelp project
 29 | 	echo.  epub       to make an epub
 30 | 	echo.  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter
 31 | 	echo.  text       to make text files
 32 | 	echo.  man        to make manual pages
 33 | 	echo.  texinfo    to make Texinfo files
 34 | 	echo.  gettext    to make PO message catalogs
 35 | 	echo.  changes    to make an overview over all changed/added/deprecated items
 36 | 	echo.  xml        to make Docutils-native XML files
 37 | 	echo.  pseudoxml  to make pseudoxml-XML files for display purposes
 38 | 	echo.  linkcheck  to check all external links for integrity
 39 | 	echo.  doctest    to run all doctests embedded in the documentation if enabled
 40 | 	goto end
 41 | )
 42 | 
 43 | if "%1" == "clean" (
 44 | 	for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
 45 | 	del /q /s %BUILDDIR%\*
 46 | 	goto end
 47 | )
 48 | 
 49 | 
 50 | %SPHINXBUILD% 2> nul
 51 | if errorlevel 9009 (
 52 | 	echo.
 53 | 	echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
 54 | 	echo.installed, then set the SPHINXBUILD environment variable to point
 55 | 	echo.to the full path of the 'sphinx-build' executable. Alternatively you
 56 | 	echo.may add the Sphinx directory to PATH.
 57 | 	echo.
 58 | 	echo.If you don't have Sphinx installed, grab it from
 59 | 	echo.http://sphinx-doc.org/
 60 | 	exit /b 1
 61 | )
 62 | 
 63 | if "%1" == "html" (
 64 | 	%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
 65 | 	if errorlevel 1 exit /b 1
 66 | 	echo.
 67 | 	echo.Build finished. The HTML pages are in %BUILDDIR%/html.
 68 | 	goto end
 69 | )
 70 | 
 71 | if "%1" == "dirhtml" (
 72 | 	%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
 73 | 	if errorlevel 1 exit /b 1
 74 | 	echo.
 75 | 	echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
 76 | 	goto end
 77 | )
 78 | 
 79 | if "%1" == "singlehtml" (
 80 | 	%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
 81 | 	if errorlevel 1 exit /b 1
 82 | 	echo.
 83 | 	echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
 84 | 	goto end
 85 | )
 86 | 
 87 | if "%1" == "pickle" (
 88 | 	%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
 89 | 	if errorlevel 1 exit /b 1
 90 | 	echo.
 91 | 	echo.Build finished; now you can process the pickle files.
 92 | 	goto end
 93 | )
 94 | 
 95 | if "%1" == "json" (
 96 | 	%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
 97 | 	if errorlevel 1 exit /b 1
 98 | 	echo.
 99 | 	echo.Build finished; now you can process the JSON files.
100 | 	goto end
101 | )
102 | 
103 | if "%1" == "htmlhelp" (
104 | 	%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
105 | 	if errorlevel 1 exit /b 1
106 | 	echo.
107 | 	echo.Build finished; now you can run HTML Help Workshop with the ^
108 | .hhp project file in %BUILDDIR%/htmlhelp.
109 | 	goto end
110 | )
111 | 
112 | if "%1" == "qthelp" (
113 | 	%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
114 | 	if errorlevel 1 exit /b 1
115 | 	echo.
116 | 	echo.Build finished; now you can run "qcollectiongenerator" with the ^
117 | .qhcp project file in %BUILDDIR%/qthelp, like this:
118 | 	echo.^> qcollectiongenerator %BUILDDIR%\qthelp\RainbowStream.qhcp
119 | 	echo.To view the help file:
120 | 	echo.^> assistant -collectionFile %BUILDDIR%\qthelp\RainbowStream.ghc
121 | 	goto end
122 | )
123 | 
124 | if "%1" == "devhelp" (
125 | 	%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
126 | 	if errorlevel 1 exit /b 1
127 | 	echo.
128 | 	echo.Build finished.
129 | 	goto end
130 | )
131 | 
132 | if "%1" == "epub" (
133 | 	%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
134 | 	if errorlevel 1 exit /b 1
135 | 	echo.
136 | 	echo.Build finished. The epub file is in %BUILDDIR%/epub.
137 | 	goto end
138 | )
139 | 
140 | if "%1" == "latex" (
141 | 	%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
142 | 	if errorlevel 1 exit /b 1
143 | 	echo.
144 | 	echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
145 | 	goto end
146 | )
147 | 
148 | if "%1" == "latexpdf" (
149 | 	%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
150 | 	cd %BUILDDIR%/latex
151 | 	make all-pdf
152 | 	cd %BUILDDIR%/..
153 | 	echo.
154 | 	echo.Build finished; the PDF files are in %BUILDDIR%/latex.
155 | 	goto end
156 | )
157 | 
158 | if "%1" == "latexpdfja" (
159 | 	%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
160 | 	cd %BUILDDIR%/latex
161 | 	make all-pdf-ja
162 | 	cd %BUILDDIR%/..
163 | 	echo.
164 | 	echo.Build finished; the PDF files are in %BUILDDIR%/latex.
165 | 	goto end
166 | )
167 | 
168 | if "%1" == "text" (
169 | 	%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
170 | 	if errorlevel 1 exit /b 1
171 | 	echo.
172 | 	echo.Build finished. The text files are in %BUILDDIR%/text.
173 | 	goto end
174 | )
175 | 
176 | if "%1" == "man" (
177 | 	%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
178 | 	if errorlevel 1 exit /b 1
179 | 	echo.
180 | 	echo.Build finished. The manual pages are in %BUILDDIR%/man.
181 | 	goto end
182 | )
183 | 
184 | if "%1" == "texinfo" (
185 | 	%SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
186 | 	if errorlevel 1 exit /b 1
187 | 	echo.
188 | 	echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
189 | 	goto end
190 | )
191 | 
192 | if "%1" == "gettext" (
193 | 	%SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
194 | 	if errorlevel 1 exit /b 1
195 | 	echo.
196 | 	echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
197 | 	goto end
198 | )
199 | 
200 | if "%1" == "changes" (
201 | 	%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
202 | 	if errorlevel 1 exit /b 1
203 | 	echo.
204 | 	echo.The overview file is in %BUILDDIR%/changes.
205 | 	goto end
206 | )
207 | 
208 | if "%1" == "linkcheck" (
209 | 	%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
210 | 	if errorlevel 1 exit /b 1
211 | 	echo.
212 | 	echo.Link check complete; look for any errors in the above output ^
213 | or in %BUILDDIR%/linkcheck/output.txt.
214 | 	goto end
215 | )
216 | 
217 | if "%1" == "doctest" (
218 | 	%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
219 | 	if errorlevel 1 exit /b 1
220 | 	echo.
221 | 	echo.Testing of doctests in the sources finished, look at the ^
222 | results in %BUILDDIR%/doctest/output.txt.
223 | 	goto end
224 | )
225 | 
226 | if "%1" == "xml" (
227 | 	%SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
228 | 	if errorlevel 1 exit /b 1
229 | 	echo.
230 | 	echo.Build finished. The XML files are in %BUILDDIR%/xml.
231 | 	goto end
232 | )
233 | 
234 | if "%1" == "pseudoxml" (
235 | 	%SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
236 | 	if errorlevel 1 exit /b 1
237 | 	echo.
238 | 	echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
239 | 	goto end
240 | )
241 | 
242 | :end
243 | 


--------------------------------------------------------------------------------
/rainbowstream/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/orakaro/rainbowstream/8b2efa52574865646adfb157f4563a72d9e37d96/rainbowstream/__init__.py


--------------------------------------------------------------------------------
/rainbowstream/c_image.py:
--------------------------------------------------------------------------------
  1 | # -*- coding: utf-8 -*-
  2 | from PIL import Image
  3 | from os.path import join, dirname, getmtime, exists, expanduser
  4 | from .config import *
  5 | from .py3patch import *
  6 | 
  7 | import ctypes
  8 | import sys
  9 | import os
 10 | 
 11 | 
 12 | def call_c():
 13 |     """
 14 |     Call the C program for converting RGB to Ansi colors
 15 |     """
 16 |     library = expanduser('~/.image.so')
 17 |     sauce = join(dirname(__file__), 'image.c')
 18 |     if not exists(library) or getmtime(sauce) > getmtime(library):
 19 |         build = "cc -fPIC -shared -o %s %s" % (library, sauce)
 20 |         os.system(build + " >/dev/null 2>&1")
 21 |     image_c = ctypes.cdll.LoadLibrary(library)
 22 |     image_c.init()
 23 |     return image_c.rgb_to_ansi
 24 | 
 25 | rgb2short = call_c()
 26 | 
 27 | 
 28 | def pixel_print(pixel):
 29 |     """
 30 |     Print a pixel with given Ansi color
 31 |     """
 32 |     r, g, b = pixel[:3]
 33 | 
 34 |     if c['24BIT'] is True:
 35 |         sys.stdout.write('\033[48;2;%d;%d;%dm \033[0m'
 36 |                          % (r, g, b))
 37 |     else:
 38 |         ansicolor = rgb2short(r, g, b)
 39 |         sys.stdout.write('\033[48;5;%sm \033[0m' % (ansicolor))
 40 | 
 41 | 
 42 | def block_print(higher, lower):
 43 |     """
 44 |     Print two pixels arranged above each other with Ansi color.
 45 |     Abuses Unicode to print two pixels in the space of one terminal block.
 46 |     """
 47 |     r0, g0, b0 = lower[:3]
 48 |     r1, g1, b1 = higher[:3]
 49 | 
 50 |     if c['24BIT'] is True:
 51 |         sys.stdout.write('\033[38;2;%d;%d;%dm\033[48;2;%d;%d;%dm▄\033[0m'
 52 |                          % (r1, g1, b1, r0, g0, b0))
 53 |     else:
 54 |         i0 = rgb2short(r0, g0, b0)
 55 |         i1 = rgb2short(r1, g1, b1)
 56 |         sys.stdout.write('\033[38;5;%sm\033[48;5;%sm▄\033[0m' % (i1, i0))
 57 | 
 58 | 
 59 | def image_to_display(path, start=None, length=None):
 60 |     """
 61 |     Display an image
 62 |     """
 63 |     rows, columns = os.popen('stty size', 'r').read().split()
 64 |     if not start:
 65 |         start = c['IMAGE_SHIFT']
 66 |     if not length:
 67 |         length = int(columns) - 2 * start
 68 |     i = Image.open(path)
 69 |     i = i.convert('RGBA')
 70 |     w, h = i.size
 71 |     i.load()
 72 |     width = min(w, length)
 73 |     height = int(float(h) * (float(width) / float(w)))
 74 | 
 75 |     if c['IMAGE_RESIZE_TO_FIT'] is True:
 76 |         # If it image won't fit in the terminal without scrolling shrink it
 77 |         # Subtract 3 from rows so the tweet message fits in too.
 78 |         h = 2 * (int(rows) - 3)
 79 |         if height >= h:
 80 |             width = int(float(width) * (float(h) / float(height)))
 81 |             height = h
 82 |     if (height <= 0) or (width <= 0):
 83 |         raise ValueError("image has negative dimensions")
 84 | 
 85 |     height = min(height, c['IMAGE_MAX_HEIGHT'])
 86 | 
 87 |     # Sixel
 88 |     if c['IMAGE_ON_TERM'] == 'sixel':
 89 |         import fcntl, struct, termios
 90 |         from io import BytesIO
 91 |         from libsixel import sixel_dither_new, sixel_dither_initialize, sixel_encode, sixel_output_new, SIXEL_PIXELFORMAT_RGBA8888
 92 |         from resizeimage import resizeimage
 93 | 
 94 |         # FIXME: rows and columns are gotten a second time. Maybe use this at 
 95 |         # the begining of function instead of the call to stty size
 96 |         farg = struct.pack("HHHH", 0, 0, 0, 0)
 97 |         fd_stdout = sys.stdout.fileno()
 98 |         fretint = fcntl.ioctl(fd_stdout, termios.TIOCGWINSZ, farg)
 99 |         rows, columns, xpixels, ypixels = struct.unpack("HHHH", fretint)
100 |         max_width_pixels = width * (xpixels // columns)
101 |         max_height_pixels = height * (ypixels // rows)
102 | 
103 |         # FIXME: This way is preferable to avoid an addition dependency, but it doesn't work correctly
104 |         # i = i.resize((max_width_pixels, max_height_pixels), Image.ANTIALIAS)
105 |         i = resizeimage.resize_thumbnail(i, [max_width_pixels, max_height_pixels])
106 | 
107 |         sixel = BytesIO()
108 |         dither = sixel_dither_new(256)
109 |         sixel_dither_initialize(dither, i.tobytes(), i.width, i.height, SIXEL_PIXELFORMAT_RGBA8888)
110 |         sixel_encode(i.tobytes(), i.width, i.height, 1, dither, 
111 |                 sixel_output_new(lambda imgdata, sixel: sixel.write(imgdata), sixel))
112 |         sys.stdout.write('%s%s' % (' ' * start, sixel.getvalue().decode('ascii')))
113 | 
114 |     # SGR
115 |     else:
116 |         i = i.resize((width, height), Image.ANTIALIAS)
117 | 
118 |         for real_y in xrange(height // 2):
119 |             sys.stdout.write(' ' * start)
120 |             for x in xrange(width):
121 |                 y = real_y * 2
122 |                 p0 = i.getpixel((x, y))
123 |                 p1 = i.getpixel((x, y + 1))
124 |                 block_print(p1, p0)
125 |             sys.stdout.write('\n')
126 | 
127 | """
128 | For direct using purpose
129 | """
130 | if __name__ == '__main__':
131 |     image_to_display(sys.argv[1])
132 | 


--------------------------------------------------------------------------------
/rainbowstream/colors.py:
--------------------------------------------------------------------------------
 1 | def basic_color(code):
 2 |     """
 3 |     16 colors supported
 4 |     """
 5 |     def inner(text, rl=False):
 6 |         """
 7 |         Every raw_input with color sequences should be called with
 8 |         rl=True to avoid readline messed up the length calculation
 9 |         """
10 |         c = code
11 |         if rl:
12 |             return "\001\033[%sm\002%s\001\033[0m\002" % (c, text)
13 |         else:
14 |             return "\033[%sm%s\033[0m" % (c, text)
15 |     return inner
16 | 
17 | 
18 | def term_color(code):
19 |     """
20 |     256 colors supported
21 |     """
22 |     def inner(text, rl=False):
23 |         """
24 |         Every raw_input with color sequences should be called with
25 |         rl=True to avoid readline messed up the length calculation
26 |         """
27 |         c = code
28 |         if rl:
29 |             return "\001\033[38;5;%sm\002%s\001\033[0m\002" % (c, text)
30 |         else:
31 |             return "\033[38;5;%sm%s\033[0m" % (c, text)
32 |     return inner
33 | 
34 | 
35 | """
36 | 16 basic colors
37 | """
38 | default = basic_color('39')
39 | black = basic_color('30')
40 | red = basic_color('31')
41 | green = basic_color('32')
42 | yellow = basic_color('33')
43 | blue = basic_color('34')
44 | magenta = basic_color('35')
45 | cyan = basic_color('36')
46 | grey = basic_color('90')
47 | light_red = basic_color('91')
48 | light_green = basic_color('92')
49 | light_yellow = basic_color('93')
50 | light_blue = basic_color('94')
51 | light_magenta = basic_color('95')
52 | light_cyan = basic_color('96')
53 | white = basic_color('97')
54 | 
55 | """
56 | 16 basic colors on background
57 | """
58 | on_default = basic_color('49')
59 | on_black = basic_color('40')
60 | on_red = basic_color('41')
61 | on_green = basic_color('42')
62 | on_yellow = basic_color('43')
63 | on_blue = basic_color('44')
64 | on_magenta = basic_color('45')
65 | on_cyan = basic_color('46')
66 | on_grey = basic_color('100')
67 | on_light_red = basic_color('101')
68 | on_light_green = basic_color('102')
69 | on_light_yellow = basic_color('103')
70 | on_light_blue = basic_color('104')
71 | on_light_magenta = basic_color('105')
72 | on_light_cyan = basic_color('106')
73 | on_white = basic_color('107')
74 | 


--------------------------------------------------------------------------------
/rainbowstream/colorset/base16.json:
--------------------------------------------------------------------------------
 1 | {
 2 |     "DECORATED_NAME": 7,
 3 |     "CYCLE_COLOR": [1,2,3,4,5,6,16,17,20,21],
 4 |     "TWEET": {
 5 |         "mynick": 3,
 6 |         "nick": 4,
 7 |         "clock": 8,
 8 |         "id": 4,
 9 |         "client": 7,
10 |         "favorited": 1,
11 |         "retweet_count": 2,
12 |         "favorite_count": 1,
13 |         "rt": 2,
14 |         "link": 4,
15 |         "hashtag": 3,
16 |         "mytweet": 20,
17 |         "keyword": "on_light_green"
18 |     },
19 |     "NOTIFICATION": {
20 |         "source_nick": 3,
21 |         "notify": 1,
22 |         "clock": 8
23 |     },
24 |     "MESSAGE": {
25 |         "partner": 2,
26 |         "me": 7,
27 |         "partner_frame": 2,
28 |         "me_frame": 7,
29 |         "sender": 2,
30 |         "recipient": 6,
31 |         "to": 3,
32 |         "clock": 8,
33 |         "id": 4
34 |     },
35 |     "PROFILE": {
36 |         "statuses_count": 6,
37 |         "friends_count": 7,
38 |         "followers_count": 7,
39 |         "nick": 2,
40 |         "profile_image_url": 4,
41 |         "description": 7,
42 |         "location": 7,
43 |         "url": 4,
44 |         "clock": 8
45 |     },
46 |     "TREND": {
47 |         "url": 6
48 |     },
49 |     "CAL": {
50 |         "days": 1,
51 |         "today": "on_light_blue"
52 |     },
53 |     "GROUP": {
54 |         "name": 6,
55 |         "member": 1,
56 |         "subscriber": 3,
57 |         "mode": 6,
58 |         "description": 2,
59 |         "clock": 8
60 |     }
61 | }
62 | 


--------------------------------------------------------------------------------
/rainbowstream/colorset/config:
--------------------------------------------------------------------------------
 1 | {
 2 |     //Time in seconds between each automatic poll. Most Twitter accounts have a limit of 15 requests each 15 minutes. If in doubt, set it to 90 or more. 
 3 |     "POLLING_TIME" : 90,
 4 |     // Turn to 'true' in order to disable extended tweets display (legacy mode)
 5 |     "DISABLE_EXTENDED_TWEETS" : false,
 6 |     // After 120 minutes, the stream will automatically hangup
 7 |     "HEARTBEAT_TIMEOUT" : 120,
 8 |     // Image on term
 9 |     "IMAGE_ON_TERM" : false,
10 |     // Resize image to fit on terminal view
11 |     "IMAGE_RESIZE_TO_FIT" : false,
12 |     // Themes
13 |     "THEME" : "monokai",
14 |     // Ascii Art
15 |     "ASCII_ART" : true,
16 |     // Hide promt when receive a tweet
17 |     "HIDE_PROMPT" : true,
18 |     // Prefix
19 |     "PREFIX" : "#owner#place#me#keyword",
20 |     // 'search': search type ('mixed','recent','popular')
21 |     "SEARCH_TYPE" : "mixed",
22 |     // 'search': search max result, number over 100 will fallback to 100
23 |     "SEARCH_MAX_RECORD" : 5,
24 |     // 'home': default number of home's tweets
25 |     "HOME_TWEET_NUM" : 5,
26 |     // 'allrt': default number of retweets
27 |     "RETWEETS_SHOW_NUM" : 5,
28 |     // 'conversation': max tweet in a thread
29 |     "CONVERSATION_MAX" : 30,
30 |     // 'quote' format
31 |     "QUOTE_FORMAT" : "#comment https://twitter.com/#owner/status/#tid",
32 |     // 'thread' meta format
33 |     "THREAD_META_LEFT" : "(#id) #clock",
34 |     "THREAD_META_RIGHT" : "#clock (#id)",
35 |     // 'thread' frame's minimum width
36 |     "THREAD_MIN_WIDTH" : 20,
37 |     // 'Notification' format
38 |     "NOTIFY_FORMAT" : "  #source_user #notify #clock",
39 |     // 'inbox','sent': default number of direct message
40 |     "MESSAGES_DISPLAY" : 5,
41 |     // 'trend': max trending topics
42 |     "TREND_MAX" : 10,
43 |     // List home timeline max
44 |     "LIST_MAX" : 5,
45 |     // 'switch': Filter and Ignore list ex: ['@fat','@mdo']
46 |     "ONLY_LIST" : [],
47 |     "IGNORE_LIST" : [],
48 |     // Autocomplete history file name
49 |     "HISTORY_FILENAME" : "completer.hist",
50 |     // Image margin
51 |     "IMAGE_SHIFT" : 2,
52 |     // Image max height
53 |     "IMAGE_MAX_HEIGHT" : 90,
54 |     // Seconds to wait before displaying another tweet, will drop all tweets while waiting.
55 |     "STREAM_DELAY" : 0,
56 |     // Stream config
57 |     "USER_DOMAIN" : "userstream.twitter.com",
58 |     "PUBLIC_DOMAIN" : "stream.twitter.com",
59 |     "SITE_DOMAIN" : "sitestream.twitter.com",
60 |     // Format
61 |     "FORMAT": {
62 |         "TWEET": {
63 |             "CLOCK_FORMAT" : "%Y/%m/%d %H:%M:%S",
64 |             "DISPLAY" : "\n  #name #nick #clock \n  \u267A:#rt_count \u2665:#fa_count id:#id via #client #fav\n  #tweet"
65 |         },
66 |         "MESSAGE": {
67 |             "CLOCK_FORMAT" : "%Y/%m/%d %H:%M:%S",
68 |             "DISPLAY" : "\n  #sender_name #sender_nick #to #recipient_name #recipient_nick :\n  #clock message_id:#id\n  #message"
69 |         }
70 |     },
71 |     "POCKET_SUPPORT": false
72 | }
73 | 


--------------------------------------------------------------------------------
/rainbowstream/colorset/larapaste.json:
--------------------------------------------------------------------------------
 1 | {
 2 |     /* Color config
 3 |     There are 16 basic colors supported :
 4 |         * default
 5 |         * black
 6 |         * red
 7 |         * green
 8 |         * yellow
 9 |         * blue
10 |         * magenta
11 |         * cyan
12 |         * grey
13 |         * light_red
14 |         * light_green
15 |         * light_yellow
16 |         * light_blue
17 |         * light_magenta
18 |         * light_cyan
19 |         * white
20 |     and 256 terminal's colors from 0 to 255
21 |     */
22 | 
23 |     "DECORATED_NAME" : 37,
24 |     "CYCLE_COLOR" :[37,184,202,154,59,230],
25 |     "TWEET" : {
26 |         "mynick"          : 202,
27 |         "nick"            : 37,
28 |         "clock"           : 184,
29 |         "id"              : 184,
30 |         "client"          : 154,
31 |         "favorited"       : 154,
32 |         "retweet_count"   : 154,
33 |         "favorite_count"  : 202,
34 |         "rt"              : 202,
35 |         "link"            : 154,
36 |         "hashtag"         : 37,
37 |         "mytweet"         : 202,
38 |         "keyword"         : "on_light_green"
39 |     },
40 | 
41 |     "NOTIFICATION":{
42 |         "source_nick"   : 37,
43 |         "notify"        : 202,
44 |         "clock"         : 184
45 |     },
46 | 
47 |     "MESSAGE" : {
48 |         "partner"       : 37,
49 |         "me"            : 37,
50 |         "partner_frame" : 154,
51 |         "me_frame"      : 202,
52 |         "sender"        : 37,
53 |         "recipient"     : 37,
54 |         "to"            : 154,
55 |         "clock"         : 184,
56 |         "id"            : 202
57 |     },
58 | 
59 |     "PROFILE" : {
60 |         "statuses_count"    : 202,
61 |         "friends_count"     : 37,
62 |         "followers_count"   : 184,
63 |         "nick"              : 37,
64 |         "profile_image_url" : 154,
65 |         "description"       : 230,
66 |         "location"          : 59,
67 |         "url"               : 154,
68 |         "clock"             : 184
69 |     },
70 | 
71 |     "TREND" : {
72 |         "url": 154
73 |     },
74 | 
75 |     "CAL" : {
76 |         "days": 202,
77 |         "today": "on_light_magenta"
78 |     },
79 | 
80 |     "GROUP" : {
81 |         "name": 37,
82 |         "member": 184,
83 |         "subscriber": 37,
84 |         "mode": 59,
85 |         "description": 230,
86 |         "clock": 184
87 |     }
88 | }
89 | 


--------------------------------------------------------------------------------
/rainbowstream/colorset/monokai.json:
--------------------------------------------------------------------------------
 1 | {
 2 |     /* Color config
 3 |     There are 16 basic colors supported :
 4 |         * default
 5 |         * black
 6 |         * red
 7 |         * green
 8 |         * yellow
 9 |         * blue
10 |         * magenta
11 |         * cyan
12 |         * grey
13 |         * light_red
14 |         * light_green
15 |         * light_yellow
16 |         * light_blue
17 |         * light_magenta
18 |         * light_cyan
19 |         * white
20 |     and 256 terminal's colors from 0 to 255
21 |     */
22 | 
23 |     "DECORATED_NAME" : 198,
24 |     "CYCLE_COLOR" :[198,57,166,50,179,74,112],
25 |     "TWEET" : {
26 |         "mynick"          : 179,
27 |         "nick"            : 112,
28 |         "clock"           : 57,
29 |         "id"              : 166,
30 |         "client"          : 74,
31 |         "favorited"       : 50,
32 |         "retweet_count"   : 50,
33 |         "favorite_count"  : 198,
34 |         "rt"              : 179,
35 |         "link"            : 74,
36 |         "hashtag"         : 198,
37 |         "mytweet"         : 179,
38 |         "keyword"         : "on_light_green"
39 |     },
40 | 
41 |     "NOTIFICATION":{
42 |         "source_nick"   : 112,
43 |         "notify"        : 179,
44 |         "clock"         : 57
45 |     },
46 | 
47 |     "MESSAGE" : {
48 |         "partner"       : 112,
49 |         "me"            : 112,
50 |         "partner_frame" : 198,
51 |         "me_frame"      : 74,
52 |         "sender"        : 112,
53 |         "recipient"     : 112,
54 |         "to"            : 50,
55 |         "clock"         : 57,
56 |         "id"            : 166
57 |     },
58 | 
59 |     "PROFILE" : {
60 |         "statuses_count"    : 112,
61 |         "friends_count"     : 198,
62 |         "followers_count"   : 57,
63 |         "nick"              : 198,
64 |         "profile_image_url" : 74,
65 |         "description"       : 166,
66 |         "location"          : 112,
67 |         "url"               : 74,
68 |         "clock"             : 57
69 |     },
70 | 
71 |     "TREND" : {
72 |         "url": 74
73 |     },
74 | 
75 |     "CAL" : {
76 |         "days": 57,
77 |         "today": "on_light_blue"
78 |     },
79 | 
80 |     "GROUP" : {
81 |         "name": 112,
82 |         "member": 57,
83 |         "subscriber": 198,
84 |         "mode": 112,
85 |         "description": 166,
86 |         "clock": 57
87 |     }
88 | }
89 | 


--------------------------------------------------------------------------------
/rainbowstream/colorset/solarized.json:
--------------------------------------------------------------------------------
 1 | {
 2 |     /* Color config
 3 |     There are 16 basic colors supported :
 4 |         * default
 5 |         * black
 6 |         * red
 7 |         * green
 8 |         * yellow
 9 |         * blue
10 |         * magenta
11 |         * cyan
12 |         * grey
13 |         * light_red
14 |         * light_green
15 |         * light_yellow
16 |         * light_blue
17 |         * light_magenta
18 |         * light_cyan
19 |         * white
20 |     and 256 terminal's colors from 0 to 255
21 |     */
22 | 
23 |     "DECORATED_NAME" : 64,
24 |     "CYCLE_COLOR" :[124,32,64,66,130,23],
25 |     "TWEET" : {
26 |         "mynick"          : 66,
27 |         "nick"            : 64,
28 |         "clock"           : 32,
29 |         "id"              : 130,
30 |         "client"          : 23,
31 |         "favorited"       : 64,
32 |         "retweet_count"   : 64,
33 |         "favorite_count"  : 124,
34 |         "rt"              : 66,
35 |         "link"            : 23,
36 |         "hashtag"         : 64,
37 |         "mytweet"         : 66,
38 |         "keyword"         : "on_light_green"
39 |     },
40 | 
41 |     "NOTIFICATION":{
42 |         "source_nick"   : 64,
43 |         "notify"        : 66,
44 |         "clock"         : 32
45 |     },
46 | 
47 |     "MESSAGE" : {
48 |         "partner"       : 64,
49 |         "me"            : 64,
50 |         "partner_frame" : 124,
51 |         "me_frame"      : 23,
52 |         "sender"        : 64,
53 |         "recipient"     : 64,
54 |         "to"            : 130,
55 |         "clock"         : 32,
56 |         "id"            : 124
57 |     },
58 | 
59 |     "PROFILE" : {
60 |         "statuses_count"    : 124,
61 |         "friends_count"     : 32,
62 |         "followers_count"   : 130,
63 |         "nick"              : 64,
64 |         "profile_image_url" : 23,
65 |         "description"       : 66,
66 |         "location"          : 64,
67 |         "url"               : 23,
68 |         "clock"             : 32
69 |     },
70 | 
71 |     "TREND" : {
72 |         "url": 23
73 |     },
74 | 
75 |     "CAL" : {
76 |         "days": 64,
77 |         "today": "light_green"
78 |     },
79 | 
80 |     "GROUP" : {
81 |         "name": 64,
82 |         "member": 130,
83 |         "subscriber": 32,
84 |         "mode": 164,
85 |         "description": 66,
86 |         "clock": 32
87 |     }
88 | }
89 | 


--------------------------------------------------------------------------------
/rainbowstream/colorset/tomorrow_night.json:
--------------------------------------------------------------------------------
 1 | {
 2 |     /* Color config
 3 |     There are 16 basic colors supported :
 4 |         * default
 5 |         * black
 6 |         * red
 7 |         * green
 8 |         * yellow
 9 |         * blue
10 |         * magenta
11 |         * cyan
12 |         * grey
13 |         * light_red
14 |         * light_green
15 |         * light_yellow
16 |         * light_blue
17 |         * light_magenta
18 |         * light_cyan
19 |         * white
20 |     and 256 terminal's colors from 0 to 255
21 |     */
22 | 
23 |     "DECORATED_NAME" : 67,
24 |     "CYCLE_COLOR" :[67,166,30,97,58,179,145],
25 |     "TWEET" : {
26 |         "mynick"          : 145,
27 |         "nick"            : 67,
28 |         "clock"           : 58,
29 |         "id"              : 58,
30 |         "client"          : 58,
31 |         "favorited"       : 97,
32 |         "retweet_count"   : 97,
33 |         "favorite_count"  : 166,
34 |         "rt"              : 145,
35 |         "link"            : 30,
36 |         "hashtag"         : 67,
37 |         "mytweet"         : 145,
38 |         "keyword"         : "on_light_blue"
39 |     },
40 | 
41 |     "NOTIFICATION":{
42 |         "source_nick"   : 67,
43 |         "notify"        : 145,
44 |         "clock"         : 58
45 |     },
46 | 
47 |     "MESSAGE" : {
48 |         "partner"       : 67,
49 |         "me"            : 67,
50 |         "partner_frame" : 166,
51 |         "me_frame"      : 30,
52 |         "sender"        : 67,
53 |         "recipient"     : 67,
54 |         "to"            : 97,
55 |         "clock"         : 58,
56 |         "id"            : 166
57 |     },
58 | 
59 |     "PROFILE" : {
60 |         "statuses_count"    : 166,
61 |         "friends_count"     : 30,
62 |         "followers_count"   : 179,
63 |         "nick"              : 67,
64 |         "profile_image_url" : 30,
65 |         "description"       : 58,
66 |         "location"          : 97,
67 |         "url"               : 30,
68 |         "clock"             : 58
69 |     },
70 | 
71 |     "TREND" : {
72 |         "url": 30
73 |     },
74 | 
75 |     "CAL" : {
76 |         "days": 67,
77 |         "today": "on_light_blue"
78 |     },
79 | 
80 |     "GROUP" : {
81 |         "name": 67,
82 |         "member": 179,
83 |         "subscriber": 30,
84 |         "mode": 97,
85 |         "description": 58,
86 |         "clock": 166
87 |     }
88 | }
89 | 


--------------------------------------------------------------------------------
/rainbowstream/config.py:
--------------------------------------------------------------------------------
  1 | import json
  2 | import re
  3 | import os
  4 | import os.path
  5 | from collections import OrderedDict
  6 | 
  7 | # Regular expression for comments in config file
  8 | comment_re = re.compile(
  9 |     '(^)[^\S\n]*/(?:\*(.*?)\*/[^\S\n]*|/[^\n]*)($)?',
 10 |     re.DOTALL | re.MULTILINE
 11 | )
 12 | 
 13 | # Config dictionary
 14 | c = {}
 15 | 
 16 | def user_filepath():
 17 |     """
 18 |     Determine the user's config file location
 19 |     """
 20 |     if 'RAINBOW_CONFIG' in os.environ:
 21 |         return os.environ['RAINBOW_CONFIG']
 22 | 
 23 |     return os.path.expanduser("~") + os.sep + '.rainbow_config.json'
 24 | 
 25 | def fixup(adict, k, v):
 26 |     """
 27 |     Fix up a key in json format
 28 |     """
 29 |     for key in adict.keys():
 30 |         if key == k:
 31 |             adict[key] = v
 32 |         elif isinstance(adict[key], dict):
 33 |             fixup(adict[key], k, v)
 34 | 
 35 | 
 36 | def load_config(filepath):
 37 |     """
 38 |     Load config from filepath
 39 |     """
 40 |     with open(filepath) as f:
 41 |         content = ''.join(f.readlines())
 42 |         match = comment_re.search(content)
 43 |         while match:
 44 |             content = content[:match.start()] + content[match.end():]
 45 |             match = comment_re.search(content)
 46 |     return json.loads(content, object_pairs_hook=OrderedDict)
 47 | 
 48 | 
 49 | def get_all_config():
 50 |     """
 51 |     Get all config
 52 |     """
 53 |     try:
 54 |         path = user_filepath()
 55 |         data = load_config(path)
 56 |         # Hard to set from prompt
 57 |         data.pop('ONLY_LIST', None)
 58 |         data.pop('IGNORE_LIST', None)
 59 |         data.pop('FORMAT', None)
 60 |         data.pop('QUOTE_FORMAT', None)
 61 |         data.pop('NOTIFY_FORMAT', None)
 62 |         return data
 63 |     except:
 64 |         return []
 65 | 
 66 | 
 67 | def get_default_config(key):
 68 |     """
 69 |     Get default value of a config key
 70 |     """
 71 |     try:
 72 |         path = os.path.dirname(__file__) + '/colorset/config'
 73 |         data = load_config(path)
 74 |         return data[key]
 75 |     except:
 76 |         raise Exception('This config key does not exist in default.')
 77 | 
 78 | 
 79 | def get_config(key):
 80 |     """
 81 |     Get current value of a config key
 82 |     """
 83 |     return c[key]
 84 | 
 85 | 
 86 | def set_config(key, value):
 87 |     """
 88 |     Set a config key with specific value
 89 |     """
 90 |     # Modify value
 91 |     if value.isdigit():
 92 |         value = int(value)
 93 |     elif value.lower() == 'true':
 94 |         value = True
 95 |     elif value.lower() == 'false':
 96 |         value = False
 97 |     # Update global config
 98 |     c[key] = value
 99 |     # Load current user config
100 |     path = user_filepath()
101 |     data = {}
102 |     try:
103 |         data = load_config(path)
104 |     except:
105 |         return
106 |     # Update config file
107 |     if key in data:
108 |         fixup(data, key, value)
109 |     else:
110 |         data[key] = value
111 |     # Save
112 |     with open(path, 'w') as out:
113 |         json.dump(data, out, indent=4)
114 |     os.system('chmod 777 ' + path)
115 | 
116 | 
117 | def delete_config(key):
118 |     """
119 |     Delete a config key
120 |     """
121 |     path = user_filepath()
122 |     try:
123 |         data = load_config(path)
124 |     except:
125 |         raise Exception('Config file is messed up.')
126 |     # Drop key
127 |     if key in data and key in c:
128 |         data.pop(key)
129 |         c.pop(key)
130 |         try:
131 |             data[key] = c[key] = get_default_config(key)
132 |         except:
133 |             pass
134 |     else:
135 |         raise Exception('No such config key.')
136 |     # Save
137 |     with open(path, 'w') as out:
138 |         json.dump(data, out, indent=4)
139 |     os.system('chmod 777 ' + path)
140 | 
141 | 
142 | def reload_config():
143 |     """
144 |     Reload config
145 |     """
146 |     try:
147 |         rainbow_config = user_filepath()
148 |         data = load_config(rainbow_config)
149 |         for d in data:
150 |             c[d] = data[d]
151 |     except:
152 |         raise Exception('Can not reload config file with wrong format.')
153 | 
154 | 
155 | def init_config():
156 |     """
157 |     Init configuration
158 |     """
159 |     # Load the initial config
160 |     config = os.path.dirname(__file__) + \
161 |         '/colorset/config'
162 |     try:
163 |         data = load_config(config)
164 |         for d in data:
165 |             c[d] = data[d]
166 |     except:
167 |         pass
168 |     # Load user's config
169 |     rainbow_config = user_filepath()
170 |     try:
171 |         data = load_config(rainbow_config)
172 |         for d in data:
173 |             c[d] = data[d]
174 |     except (IOError, ValueError) as e:
175 |         c['USER_JSON_ERROR'] = str(e)
176 |     # Load default theme
177 |     theme_file = os.path.dirname(__file__) + \
178 |         '/colorset/' + c['THEME'] + '.json'
179 |     try:
180 |         data = load_config(theme_file)
181 |         for d in data:
182 |             c[d] = data[d]
183 |     except:
184 |         pass
185 | 
186 | 
187 | # Init config
188 | init_config()
189 | 


--------------------------------------------------------------------------------
/rainbowstream/draw.py:
--------------------------------------------------------------------------------
   1 | import random
   2 | import textwrap
   3 | import itertools
   4 | import requests
   5 | import locale
   6 | import arrow
   7 | import re
   8 | import os
   9 | 
  10 | from io import BytesIO
  11 | from twitter.util import printNicely
  12 | from functools import wraps
  13 | from pyfiglet import figlet_format
  14 | from dateutil import parser
  15 | from .c_image import *
  16 | from .colors import *
  17 | from .config import *
  18 | from .py3patch import *
  19 | from .emoji import *
  20 | 
  21 | # Draw global variables
  22 | dg = {}
  23 | 
  24 | 
  25 | def init_cycle():
  26 |     """
  27 |     Init the cycle
  28 |     """
  29 |     colors_shuffle = [globals()[i.encode('utf8')]
  30 |                       if not str(i).isdigit()
  31 |                       else term_color(int(i))
  32 |                       for i in c['CYCLE_COLOR']]
  33 |     return itertools.cycle(colors_shuffle)
  34 | 
  35 | 
  36 | def start_cycle():
  37 |     """
  38 |     Notify from rainbow
  39 |     """
  40 |     dg['cyc'] = init_cycle()
  41 |     dg['cache'] = {}
  42 |     dg['humanize_unsupported'] = False
  43 | 
  44 | 
  45 | def order_rainbow(s):
  46 |     """
  47 |     Print a string with ordered color with each character
  48 |     """
  49 |     colors_shuffle = [globals()[i.encode('utf8')]
  50 |                       if not str(i).isdigit()
  51 |                       else term_color(int(i))
  52 |                       for i in c['CYCLE_COLOR']]
  53 |     colored = [colors_shuffle[i % 7](s[i]) for i in xrange(len(s))]
  54 |     return ''.join(colored)
  55 | 
  56 | 
  57 | def random_rainbow(s):
  58 |     """
  59 |     Print a string with random color with each character
  60 |     """
  61 |     colors_shuffle = [globals()[i.encode('utf8')]
  62 |                       if not str(i).isdigit()
  63 |                       else term_color(int(i))
  64 |                       for i in c['CYCLE_COLOR']]
  65 |     colored = [random.choice(colors_shuffle)(i) for i in s]
  66 |     return ''.join(colored)
  67 | 
  68 | 
  69 | def Memoize(func):
  70 |     """
  71 |     Memoize decorator
  72 |     """
  73 |     @wraps(func)
  74 |     def wrapper(*args):
  75 |         if args not in dg['cache']:
  76 |             dg['cache'][args] = func(*args)
  77 |         return dg['cache'][args]
  78 |     return wrapper
  79 | 
  80 | 
  81 | @Memoize
  82 | def cycle_color(s):
  83 |     """
  84 |     Cycle the colors_shuffle
  85 |     """
  86 |     return next(dg['cyc'])(s)
  87 | 
  88 | 
  89 | def ascii_art(text):
  90 |     """
  91 |     Draw the Ascii Art
  92 |     """
  93 |     fi = figlet_format(text, font='doom')
  94 |     print('\n'.join(
  95 |         [next(dg['cyc'])(i) for i in fi.split('\n')]
  96 |     ))
  97 | 
  98 | 
  99 | def check_config():
 100 |     """
 101 |     Check if config is changed
 102 |     """
 103 |     changed = False
 104 |     data = get_all_config()
 105 |     for key in c:
 106 |         if key in data:
 107 |             if data[key] != c[key]:
 108 |                 changed = True
 109 |     if changed:
 110 |         reload_config()
 111 | 
 112 | 
 113 | def validate_theme(theme):
 114 |     """
 115 |     Validate a theme exists or not
 116 |     """
 117 |     # Theme changed check
 118 |     files = os.listdir(os.path.dirname(__file__) + '/colorset')
 119 |     themes = [f.split('.')[0] for f in files if f.split('.')[-1] == 'json']
 120 |     return theme in themes
 121 | 
 122 | 
 123 | def reload_theme(value, prev):
 124 |     """
 125 |     Check current theme and update if necessary
 126 |     """
 127 |     if value != prev:
 128 |         config = os.path.dirname(
 129 |             __file__) + '/colorset/' + value + '.json'
 130 |         # Load new config
 131 |         data = load_config(config)
 132 |         if data:
 133 |             for d in data:
 134 |                 c[d] = data[d]
 135 |         # Restart color cycle and update config
 136 |         start_cycle()
 137 |         set_config('THEME', value)
 138 |         return value
 139 |     return prev
 140 | 
 141 | 
 142 | def color_func(func_name):
 143 |     """
 144 |     Call color function base on name
 145 |     """
 146 |     if str(func_name).isdigit():
 147 |         return term_color(int(func_name))
 148 |     return globals()[func_name]
 149 | 
 150 | 
 151 | def fallback_humanize(date, fallback_format=None, use_fallback=False):
 152 |     """
 153 |     Format date with arrow and a fallback format
 154 |     """
 155 |     # Convert to local timezone
 156 |     date = arrow.get(date).to('local')
 157 |     # Set default fallback format
 158 |     if not fallback_format:
 159 |         fallback_format = '%Y/%m/%d %H:%M:%S'
 160 |     # Determine using fallback format or not by a variable
 161 |     if use_fallback:
 162 |         return date.datetime.strftime(fallback_format)
 163 |     try:
 164 |         # Use Arrow's humanize function
 165 |         lang, encode = locale.getdefaultlocale()
 166 |         clock = date.humanize(locale=lang)
 167 |     except:
 168 |         # Notice at the 1st time only
 169 |         if not dg['humanize_unsupported']:
 170 |             dg['humanize_unsupported'] = True
 171 |             printNicely(
 172 |                 light_magenta('Humanized date display method does not support your $LC_ALL.'))
 173 |         # Fallback when LC_ALL is not supported
 174 |         clock = date.datetime.strftime(fallback_format)
 175 |     return clock
 176 | 
 177 | 
 178 | def get_full_text(t):
 179 |     """Handle RTs and extended tweets to always display all the available text"""
 180 | 
 181 |     if t.get('retweeted_status'):
 182 |         rt_status = t['retweeted_status']
 183 |         if rt_status.get('extended_tweet'):
 184 |             elem = rt_status['extended_tweet']
 185 |         else:
 186 |             elem = rt_status
 187 |         rt_text = elem.get('full_text', elem.get('text'))
 188 |         t['full_text'] = 'RT @' + rt_status['user']['screen_name'] + ': ' + rt_text
 189 |     elif t.get('extended_tweet'):
 190 |         t['full_text'] = t['extended_tweet']['full_text']
 191 | 
 192 |     return t.get('full_text', t.get('text'))
 193 | 
 194 | 
 195 | def draw(t, keyword=None, humanize=True, noti=False, fil=[], ig=[]):
 196 |     """
 197 |     Draw the rainbow
 198 |     """
 199 |     # Check config
 200 |     check_config()
 201 | 
 202 |     # Retrieve tweet
 203 |     tid = t['id']
 204 | 
 205 |     text = get_full_text(t)
 206 |     screen_name = t['user']['screen_name']
 207 |     name = t['user']['name']
 208 |     created_at = t['created_at']
 209 |     favorited = t['favorited']
 210 |     retweet_count = t['retweet_count']
 211 |     favorite_count = t['favorite_count']
 212 |     client = t['source']
 213 |     date = parser.parse(created_at)
 214 |     try:
 215 |         clock_format = c['FORMAT']['TWEET']['CLOCK_FORMAT']
 216 |     except:
 217 |         clock_format = '%Y/%m/%d %H:%M:%S'
 218 |     clock = fallback_humanize(date, clock_format, not humanize)
 219 | 
 220 |     # Pull extended retweet text
 221 |     try:
 222 |         # Display as a notification
 223 |         target = t['retweeted_status']['user']['screen_name']
 224 |         if all([target == c['original_name'], not noti]):
 225 |             # Add to evens for 'notification' command
 226 |             t['event'] = 'retweet'
 227 |             c['events'].append(t)
 228 |             notify_retweet(t)
 229 |             return
 230 |     except:
 231 |         pass
 232 | 
 233 |     # Unescape HTML character
 234 |     text = unescape(text)
 235 | 
 236 |     # Get client name
 237 |     try:
 238 |         client = client.split('>')[-2].split('<')[0]
 239 |     except:
 240 |         client = None
 241 | 
 242 |     # Get expanded url
 243 |     try:
 244 |         expanded_url = []
 245 |         url = []
 246 |         urls = t['entities']['urls']
 247 |         for u in urls:
 248 |             expanded_url.append(u['expanded_url'])
 249 |             url.append(u['url'])
 250 |     except:
 251 |         expanded_url = None
 252 |         url = None
 253 | 
 254 |     # Get media
 255 |     try:
 256 |         media_url = []
 257 |         media = t['entities']['media']
 258 |         for m in media:
 259 |             media_url.append(m['media_url'])
 260 |     except:
 261 |         media_url = None
 262 | 
 263 |     # Filter and ignore
 264 |     mytweet = screen_name == c['original_name']
 265 |     screen_name = '@' + screen_name
 266 |     fil = list(set((fil or []) + c['ONLY_LIST']))
 267 |     ig = list(set((ig or []) + c['IGNORE_LIST']))
 268 |     if fil and screen_name not in fil:
 269 |         return
 270 |     if ig and screen_name in ig:
 271 |         return
 272 | 
 273 |     # Get rainbow id
 274 |     if tid not in c['tweet_dict']:
 275 |         c['tweet_dict'].append(tid)
 276 |         rid = len(c['tweet_dict']) - 1
 277 |     else:
 278 |         rid = c['tweet_dict'].index(tid)
 279 | 
 280 |     # Format info
 281 |     name = cycle_color(name)
 282 |     if mytweet:
 283 |         nick = color_func(c['TWEET']['mynick'])(screen_name)
 284 |     else:
 285 |         nick = color_func(c['TWEET']['nick'])(screen_name)
 286 |     clock = clock
 287 |     id = str(rid)
 288 |     fav = ''
 289 |     if favorited:
 290 |         fav = color_func(c['TWEET']['favorited'])(u'\u2605')
 291 | 
 292 |     tweet = text.split(' ')
 293 |     tweet = [x for x in tweet if x != '']
 294 |     # Replace url
 295 |     if expanded_url:
 296 |         for index in xrange(len(expanded_url)):
 297 |             tweet = lmap(
 298 |                 lambda x: expanded_url[index]
 299 |                 if x == url[index]
 300 |                 else x,
 301 |                 tweet)
 302 |     # Highlight RT
 303 |     tweet = lmap(
 304 |         lambda x: color_func(c['TWEET']['rt'])(x)
 305 |         if x == 'RT'
 306 |         else x,
 307 |         tweet)
 308 |     # Highlight screen_name
 309 |     tweet = lmap(
 310 |         lambda x: cycle_color(x) if x.lstrip().startswith('@') else x, tweet)
 311 |     # Highlight link
 312 |     tweet = lmap(
 313 |         lambda x: color_func(c['TWEET']['link'])(x)
 314 |         if x.lstrip().startswith('http')
 315 |         else x,
 316 |         tweet)
 317 |     # Highlight hashtag
 318 |     tweet = lmap(
 319 |         lambda x: color_func(c['TWEET']['hashtag'])(x)
 320 |         if x.lstrip().startswith('#')
 321 |         else x,
 322 |         tweet)
 323 |     # Highlight my tweet
 324 |     if mytweet:
 325 |         tweet = [color_func(c['TWEET']['mytweet'])(x)
 326 |                  for x in tweet
 327 |                  if not any([
 328 |                      x == 'RT',
 329 |                      x.lstrip().startswith('http'),
 330 |                      x.lstrip().startswith('#')])
 331 |                  ]
 332 |     # Highlight keyword
 333 |     tweet = ' '.join(tweet)
 334 |     tweet = '\n  '.join(tweet.split('\n'))
 335 |     if keyword:
 336 |         roj = re.search(keyword, tweet, re.IGNORECASE)
 337 |         if roj:
 338 |             occur = roj.group()
 339 |             ary = tweet.split(occur)
 340 |             delimiter = color_func(c['TWEET']['keyword'])(occur)
 341 |             tweet = delimiter.join(ary)
 342 | 
 343 |     # Load config formater
 344 |     formater = ''
 345 |     try:
 346 |         formater = c['FORMAT']['TWEET']['DISPLAY']
 347 |         formater = name.join(formater.split('#name'))
 348 |         formater = nick.join(formater.split('#nick'))
 349 |         formater = fav.join(formater.split('#fav'))
 350 |         formater = tweet.join(formater.split('#tweet'))
 351 |         formater = emojize(formater)
 352 |         # Change clock word
 353 |         word = [wo for wo in formater.split() if '#clock' in wo][0]
 354 |         delimiter = color_func(c['TWEET']['clock'])(
 355 |             clock.join(word.split('#clock')))
 356 |         formater = delimiter.join(formater.split(word))
 357 |         # Change id word
 358 |         word = [wo for wo in formater.split() if '#id' in wo][0]
 359 |         delimiter = color_func(c['TWEET']['id'])(id.join(word.split('#id')))
 360 |         formater = delimiter.join(formater.split(word))
 361 |         # Change retweet count word
 362 |         word = [wo for wo in formater.split() if '#rt_count' in wo][0]
 363 |         delimiter = color_func(c['TWEET']['retweet_count'])(
 364 |             str(retweet_count).join(word.split('#rt_count')))
 365 |         formater = delimiter.join(formater.split(word))
 366 |         # Change favorites count word
 367 |         word = [wo for wo in formater.split() if '#fa_count' in wo][0]
 368 |         delimiter = color_func(c['TWEET']['favorite_count'])(
 369 |             str(favorite_count).join(word.split('#fa_count')))
 370 |         formater = delimiter.join(formater.split(word))
 371 |         # Change client word
 372 |         word = [wo for wo in formater.split() if '#client' in wo][0]
 373 |         delimiter = color_func(c['TWEET']['client'])(
 374 |             client.join(word.split('#client')))
 375 |         formater = delimiter.join(formater.split(word))
 376 |     except:
 377 |         pass
 378 | 
 379 |     # Add spaces in begining of line if this is inside a notification
 380 |     if noti:
 381 |         formater = '\n  '.join(formater.split('\n'))
 382 |         # Reformat
 383 |         if formater.startswith('\n'):
 384 |             formater = formater[1:]
 385 | 
 386 |     # Draw
 387 |     printNicely(formater)
 388 | 
 389 |     # Display Image
 390 |     if c['IMAGE_ON_TERM'] and media_url:
 391 |         for mu in media_url:
 392 |             try:
 393 |                 response = requests.get(mu)
 394 |                 image_to_display(BytesIO(response.content))
 395 |             except Exception:
 396 |                 printNicely(red('Sorry, image link is broken'))
 397 | 
 398 | 
 399 | def print_threads(d):
 400 |     """
 401 |     Print threads of messages
 402 |     """
 403 |     id = 1
 404 |     rel = {}
 405 |     for partner in d:
 406 |         messages = d[partner]
 407 |         count = len(messages)
 408 |         screen_name = '@' + partner[0]
 409 |         name = partner[1]
 410 |         screen_name = color_func(c['MESSAGE']['partner'])(screen_name)
 411 |         name = cycle_color(name)
 412 |         thread_id = color_func(c['MESSAGE']['id'])('thread_id:' + str(id))
 413 |         line = ' ' * 2 + name + ' ' + screen_name + \
 414 |             ' (' + str(count) + ' message) ' + thread_id
 415 |         printNicely(line)
 416 |         rel[id] = partner
 417 |         id += 1
 418 |     dg['thread'] = d
 419 |     return rel
 420 | 
 421 | 
 422 | def print_thread(partner, me_nick, me_name):
 423 |     """
 424 |     Print a thread of messages
 425 |     """
 426 |     # Sort messages by time
 427 |     messages = dg['thread'][partner]
 428 |     messages.sort(key=lambda x: int(x['created_at']))
 429 |     # Use legacy display on non-ascii text message
 430 |     ms = [m['text'] for m in messages]
 431 |     ums = [m for m in ms if not all(ord(c) < 128 for c in m)]
 432 |     if ums:
 433 |         for m in messages:
 434 |             print_message(m)
 435 |         printNicely('')
 436 |         return
 437 |     # Print the first line
 438 |     dg['frame_margin'] = margin = 2
 439 |     partner_nick = partner[0]
 440 |     partner_name = partner[1]
 441 |     left_size = len(partner_nick) + len(partner_name) + 2
 442 |     right_size = len(me_nick) + len(me_name) + 2
 443 |     partner_nick = color_func(c['MESSAGE']['partner'])('@' + partner_nick)
 444 |     partner_name = cycle_color(partner_name)
 445 |     me_screen_name = color_func(c['MESSAGE']['me'])('@' + me_nick)
 446 |     me_name = cycle_color(me_name)
 447 |     left = ' ' * margin + partner_name + ' ' + partner_nick
 448 |     right = me_name + ' ' + me_screen_name + ' ' * margin
 449 |     h, w = os.popen('stty size', 'r').read().split()
 450 |     w = int(w)
 451 |     line = '{}{}{}'.format(
 452 |         left, ' ' * (w - left_size - right_size - 2 * margin), right)
 453 |     printNicely('')
 454 |     printNicely(line)
 455 |     printNicely('')
 456 |     # Print messages
 457 |     for m in messages:
 458 |         if m['sender_screen_name'] == me_nick:
 459 |             print_right_message(m)
 460 |         elif m['recipient_screen_name'] == me_nick:
 461 |             print_left_message(m)
 462 | 
 463 | 
 464 | def print_right_message(m):
 465 |     """
 466 |     Print a message on the right of screen
 467 |     """
 468 |     h, w = os.popen('stty size', 'r').read().split()
 469 |     w = int(w)
 470 |     frame_width = w // 3 - dg['frame_margin']
 471 |     frame_width = max(c['THREAD_MIN_WIDTH'], frame_width)
 472 |     step = frame_width - 2 * dg['frame_margin']
 473 |     slicing = textwrap.wrap(m['text'], step)
 474 |     spaces = w - frame_width - dg['frame_margin']
 475 |     dotline = ' ' * spaces + '-' * frame_width
 476 |     dotline = color_func(c['MESSAGE']['me_frame'])(dotline)
 477 |     # Draw the frame
 478 |     printNicely(dotline)
 479 |     for line in slicing:
 480 |         fill = step - len(line)
 481 |         screen_line = ' ' * spaces + '| ' + line + ' ' * fill + ' '
 482 |         if slicing[-1] == line:
 483 |             screen_line = screen_line + ' >'
 484 |         else:
 485 |             screen_line = screen_line + '|'
 486 |         screen_line = color_func(c['MESSAGE']['me_frame'])(screen_line)
 487 |         printNicely(screen_line)
 488 |     printNicely(dotline)
 489 |     # Format clock
 490 |     date = arrow.get(int(m['created_at'])/1000).to('local').datetime # Read Unixtime in miliseconds
 491 |     clock_format = '%Y/%m/%d %H:%M:%S'
 492 |     try:
 493 |         clock_format = c['FORMAT']['MESSAGE']['CLOCK_FORMAT']
 494 |     except:
 495 |         pass
 496 |     clock = date.strftime(clock_format)
 497 |     # Format id
 498 |     if m['id'] not in c['message_dict']:
 499 |         c['message_dict'].append(m['id'])
 500 |         rid = len(c['message_dict']) - 1
 501 |     else:
 502 |         rid = c['message_dict'].index(m['id'])
 503 |     id = str(rid)
 504 |     # Print meta
 505 |     formater = ''
 506 |     try:
 507 |         virtual_meta = formater = c['THREAD_META_RIGHT']
 508 |         virtual_meta = clock.join(virtual_meta.split('#clock'))
 509 |         virtual_meta = id.join(virtual_meta.split('#id'))
 510 |         # Change clock word
 511 |         word = [wo for wo in formater.split() if '#clock' in wo][0]
 512 |         delimiter = color_func(c['MESSAGE']['clock'])(
 513 |             clock.join(word.split('#clock')))
 514 |         formater = delimiter.join(formater.split(word))
 515 |         # Change id word
 516 |         word = [wo for wo in formater.split() if '#id' in wo][0]
 517 |         delimiter = color_func(c['MESSAGE']['id'])(id.join(word.split('#id')))
 518 |         formater = delimiter.join(formater.split(word))
 519 |         formater = emojize(formater)
 520 |     except Exception:
 521 |         printNicely(red('Wrong format in config.'))
 522 |         return
 523 |     meta = formater
 524 |     line = ' ' * (w - len(virtual_meta) - dg['frame_margin']) + meta
 525 |     printNicely(line)
 526 | 
 527 | 
 528 | def print_left_message(m):
 529 |     """
 530 |     Print a message on the left of screen
 531 |     """
 532 |     h, w = os.popen('stty size', 'r').read().split()
 533 |     w = int(w)
 534 |     frame_width = w // 3 - dg['frame_margin']
 535 |     frame_width = max(c['THREAD_MIN_WIDTH'], frame_width)
 536 |     step = frame_width - 2 * dg['frame_margin']
 537 |     slicing = textwrap.wrap(m['text'], step)
 538 |     spaces = dg['frame_margin']
 539 |     dotline = ' ' * spaces + '-' * frame_width
 540 |     dotline = color_func(c['MESSAGE']['partner_frame'])(dotline)
 541 |     # Draw the frame
 542 |     printNicely(dotline)
 543 |     for line in slicing:
 544 |         fill = step - len(line)
 545 |         screen_line = ' ' + line + ' ' * fill + ' |'
 546 |         if slicing[-1] == line:
 547 |             screen_line = ' ' * (spaces - 1) + '< ' + screen_line
 548 |         else:
 549 |             screen_line = ' ' * spaces + '|' + screen_line
 550 |         screen_line = color_func(c['MESSAGE']['partner_frame'])(screen_line)
 551 |         printNicely(screen_line)
 552 |     printNicely(dotline)
 553 |     # Format clock
 554 |     date = parser.parse(m['created_at'])
 555 |     date = arrow.get(date).to('local').datetime
 556 |     clock_format = '%Y/%m/%d %H:%M:%S'
 557 |     try:
 558 |         clock_format = c['FORMAT']['MESSAGE']['CLOCK_FORMAT']
 559 |     except:
 560 |         pass
 561 |     clock = date.strftime(clock_format)
 562 |     # Format id
 563 |     if m['id'] not in c['message_dict']:
 564 |         c['message_dict'].append(m['id'])
 565 |         rid = len(c['message_dict']) - 1
 566 |     else:
 567 |         rid = c['message_dict'].index(m['id'])
 568 |     id = str(rid)
 569 |     # Print meta
 570 |     formater = ''
 571 |     try:
 572 |         virtual_meta = formater = c['THREAD_META_LEFT']
 573 |         virtual_meta = clock.join(virtual_meta.split('#clock'))
 574 |         virtual_meta = id.join(virtual_meta.split('#id'))
 575 |         # Change clock word
 576 |         word = [wo for wo in formater.split() if '#clock' in wo][0]
 577 |         delimiter = color_func(c['MESSAGE']['clock'])(
 578 |             clock.join(word.split('#clock')))
 579 |         formater = delimiter.join(formater.split(word))
 580 |         # Change id word
 581 |         word = [wo for wo in formater.split() if '#id' in wo][0]
 582 |         delimiter = color_func(c['MESSAGE']['id'])(id.join(word.split('#id')))
 583 |         formater = delimiter.join(formater.split(word))
 584 |         formater = emojize(formater)
 585 |     except Exception:
 586 |         printNicely(red('Wrong format in config.'))
 587 |         return
 588 |     meta = formater
 589 |     line = ' ' * dg['frame_margin'] + meta
 590 |     printNicely(line)
 591 | 
 592 | 
 593 | def print_message(m):
 594 |     """
 595 |     Print direct message
 596 |     """
 597 |     # Retrieve message
 598 |     sender_screen_name = '@' + m['sender_screen_name']
 599 |     sender_name = m['sender_name']
 600 |     text = unescape(m['text'])
 601 |     recipient_screen_name = '@' + m['recipient_screen_name']
 602 |     recipient_name = m['recipient_name']
 603 |     mid = m['id']
 604 |     date = parser.parse(m['created_at'])
 605 |     date = arrow.get(date).to('local').datetime
 606 |     clock_format = '%Y/%m/%d %H:%M:%S'
 607 |     try:
 608 |         clock_format = c['FORMAT']['MESSAGE']['CLOCK_FORMAT']
 609 |     except:
 610 |         pass
 611 |     clock = date.strftime(clock_format)
 612 | 
 613 |     # Get rainbow id
 614 |     if mid not in c['message_dict']:
 615 |         c['message_dict'].append(mid)
 616 |         rid = len(c['message_dict']) - 1
 617 |     else:
 618 |         rid = c['message_dict'].index(mid)
 619 | 
 620 |     # Draw
 621 |     sender_name = cycle_color(sender_name)
 622 |     sender_nick = color_func(c['MESSAGE']['sender'])(sender_screen_name)
 623 |     recipient_name = cycle_color(recipient_name)
 624 |     recipient_nick = color_func(
 625 |         c['MESSAGE']['recipient'])(recipient_screen_name)
 626 |     to = color_func(c['MESSAGE']['to'])('>>>')
 627 |     clock = clock
 628 |     id = str(rid)
 629 | 
 630 |     text = ''.join(lmap(lambda x: x + '  ' if x == '\n' else x, text))
 631 | 
 632 |     # Load config formater
 633 |     try:
 634 |         formater = c['FORMAT']['MESSAGE']['DISPLAY']
 635 |         formater = sender_name.join(formater.split("#sender_name"))
 636 |         formater = sender_nick.join(formater.split("#sender_nick"))
 637 |         formater = to.join(formater.split("#to"))
 638 |         formater = recipient_name.join(formater.split("#recipient_name"))
 639 |         formater = recipient_nick.join(formater.split("#recipient_nick"))
 640 |         formater = text.join(formater.split("#message"))
 641 |         # Change clock word
 642 |         word = [wo for wo in formater.split() if '#clock' in wo][0]
 643 |         delimiter = color_func(c['MESSAGE']['clock'])(
 644 |             clock.join(word.split('#clock')))
 645 |         formater = delimiter.join(formater.split(word))
 646 |         # Change id word
 647 |         word = [wo for wo in formater.split() if '#id' in wo][0]
 648 |         delimiter = color_func(c['MESSAGE']['id'])(id.join(word.split('#id')))
 649 |         formater = delimiter.join(formater.split(word))
 650 |         formater = emojize(formater)
 651 |     except:
 652 |         printNicely(red('Wrong format in config.'))
 653 |         return
 654 | 
 655 |     # Draw
 656 |     printNicely(formater)
 657 | 
 658 | 
 659 | def notify_retweet(t):
 660 |     """
 661 |     Notify a retweet
 662 |     """
 663 |     source = t['user']
 664 |     created_at = t['created_at']
 665 |     # Format
 666 |     source_user = cycle_color(source['name']) + \
 667 |         color_func(c['NOTIFICATION']['source_nick'])(
 668 |         ' @' + source['screen_name'])
 669 |     notify = color_func(c['NOTIFICATION']['notify'])(
 670 |         'retweeted your tweet')
 671 |     date = parser.parse(created_at)
 672 |     clock = fallback_humanize(date)
 673 |     clock = color_func(c['NOTIFICATION']['clock'])(clock)
 674 |     meta = c['NOTIFY_FORMAT']
 675 |     meta = source_user.join(meta.split('#source_user'))
 676 |     meta = notify.join(meta.split('#notify'))
 677 |     meta = clock.join(meta.split('#clock'))
 678 |     meta = emojize(meta)
 679 |     # Output
 680 |     printNicely('')
 681 |     printNicely(meta)
 682 |     draw(t=t['retweeted_status'], noti=True)
 683 | 
 684 | 
 685 | def notify_favorite(e):
 686 |     """
 687 |     Notify a favorite event
 688 |     """
 689 |     # Retrieve info
 690 |     target = e['target']
 691 |     if target['screen_name'] != c['original_name']:
 692 |         return
 693 |     source = e['source']
 694 |     target_object = e['target_object']
 695 |     created_at = e['created_at']
 696 |     # Format
 697 |     source_user = cycle_color(source['name']) + \
 698 |         color_func(c['NOTIFICATION']['source_nick'])(
 699 |         ' @' + source['screen_name'])
 700 |     notify = color_func(c['NOTIFICATION']['notify'])(
 701 |         'favorited your tweet')
 702 |     date = parser.parse(created_at)
 703 |     clock = fallback_humanize(date)
 704 |     clock = color_func(c['NOTIFICATION']['clock'])(clock)
 705 |     meta = c['NOTIFY_FORMAT']
 706 |     meta = source_user.join(meta.split('#source_user'))
 707 |     meta = notify.join(meta.split('#notify'))
 708 |     meta = clock.join(meta.split('#clock'))
 709 |     meta = emojize(meta)
 710 |     # Output
 711 |     printNicely('')
 712 |     printNicely(meta)
 713 |     draw(t=target_object, noti=True)
 714 | 
 715 | 
 716 | def notify_unfavorite(e):
 717 |     """
 718 |     Notify a unfavorite event
 719 |     """
 720 |     # Retrieve info
 721 |     target = e['target']
 722 |     if target['screen_name'] != c['original_name']:
 723 |         return
 724 |     source = e['source']
 725 |     target_object = e['target_object']
 726 |     created_at = e['created_at']
 727 |     # Format
 728 |     source_user = cycle_color(source['name']) + \
 729 |         color_func(c['NOTIFICATION']['source_nick'])(
 730 |         ' @' + source['screen_name'])
 731 |     notify = color_func(c['NOTIFICATION']['notify'])(
 732 |         'unfavorited your tweet')
 733 |     date = parser.parse(created_at)
 734 |     clock = fallback_humanize(date)
 735 |     clock = color_func(c['NOTIFICATION']['clock'])(clock)
 736 |     meta = c['NOTIFY_FORMAT']
 737 |     meta = source_user.join(meta.split('#source_user'))
 738 |     meta = notify.join(meta.split('#notify'))
 739 |     meta = clock.join(meta.split('#clock'))
 740 |     meta = emojize(meta)
 741 |     # Output
 742 |     printNicely('')
 743 |     printNicely(meta)
 744 |     draw(t=target_object, noti=True)
 745 | 
 746 | 
 747 | def notify_follow(e):
 748 |     """
 749 |     Notify a follow event
 750 |     """
 751 |     # Retrieve info
 752 |     target = e['target']
 753 |     if target['screen_name'] != c['original_name']:
 754 |         return
 755 |     source = e['source']
 756 |     created_at = e['created_at']
 757 |     # Format
 758 |     source_user = cycle_color(source['name']) + \
 759 |         color_func(c['NOTIFICATION']['source_nick'])(
 760 |         ' @' + source['screen_name'])
 761 |     notify = color_func(c['NOTIFICATION']['notify'])(
 762 |         'followed you')
 763 |     date = parser.parse(created_at)
 764 |     clock = fallback_humanize(date)
 765 |     clock = color_func(c['NOTIFICATION']['clock'])(clock)
 766 |     meta = c['NOTIFY_FORMAT']
 767 |     meta = source_user.join(meta.split('#source_user'))
 768 |     meta = notify.join(meta.split('#notify'))
 769 |     meta = clock.join(meta.split('#clock'))
 770 |     meta = emojize(meta)
 771 |     # Output
 772 |     printNicely('')
 773 |     printNicely(meta)
 774 | 
 775 | 
 776 | def notify_list_member_added(e):
 777 |     """
 778 |     Notify a list_member_added event
 779 |     """
 780 |     # Retrieve info
 781 |     target = e['target']
 782 |     if target['screen_name'] != c['original_name']:
 783 |         return
 784 |     source = e['source']
 785 |     target_object = [e['target_object']]  # list of Twitter list
 786 |     created_at = e['created_at']
 787 |     # Format
 788 |     source_user = cycle_color(source['name']) + \
 789 |         color_func(c['NOTIFICATION']['source_nick'])(
 790 |         ' @' + source['screen_name'])
 791 |     notify = color_func(c['NOTIFICATION']['notify'])(
 792 |         'added you to a list')
 793 |     date = parser.parse(created_at)
 794 |     clock = fallback_humanize(date)
 795 |     clock = color_func(c['NOTIFICATION']['clock'])(clock)
 796 |     meta = c['NOTIFY_FORMAT']
 797 |     meta = source_user.join(meta.split('#source_user'))
 798 |     meta = notify.join(meta.split('#notify'))
 799 |     meta = clock.join(meta.split('#clock'))
 800 |     meta = emojize(meta)
 801 |     # Output
 802 |     printNicely('')
 803 |     printNicely(meta)
 804 |     print_list(target_object, noti=True)
 805 | 
 806 | 
 807 | def notify_list_member_removed(e):
 808 |     """
 809 |     Notify a list_member_removed event
 810 |     """
 811 |     # Retrieve info
 812 |     target = e['target']
 813 |     if target['screen_name'] != c['original_name']:
 814 |         return
 815 |     source = e['source']
 816 |     target_object = [e['target_object']]  # list of Twitter list
 817 |     created_at = e['created_at']
 818 |     # Format
 819 |     source_user = cycle_color(source['name']) + \
 820 |         color_func(c['NOTIFICATION']['source_nick'])(
 821 |         ' @' + source['screen_name'])
 822 |     notify = color_func(c['NOTIFICATION']['notify'])(
 823 |         'removed you from a list')
 824 |     date = parser.parse(created_at)
 825 |     clock = fallback_humanize(date)
 826 |     clock = color_func(c['NOTIFICATION']['clock'])(clock)
 827 |     meta = c['NOTIFY_FORMAT']
 828 |     meta = source_user.join(meta.split('#source_user'))
 829 |     meta = notify.join(meta.split('#notify'))
 830 |     meta = clock.join(meta.split('#clock'))
 831 |     meta = emojize(meta)
 832 |     # Output
 833 |     printNicely('')
 834 |     printNicely(meta)
 835 |     print_list(target_object, noti=True)
 836 | 
 837 | 
 838 | def notify_list_user_subscribed(e):
 839 |     """
 840 |     Notify a list_user_subscribed event
 841 |     """
 842 |     # Retrieve info
 843 |     target = e['target']
 844 |     if target['screen_name'] != c['original_name']:
 845 |         return
 846 |     source = e['source']
 847 |     target_object = [e['target_object']]  # list of Twitter list
 848 |     created_at = e['created_at']
 849 |     # Format
 850 |     source_user = cycle_color(source['name']) + \
 851 |         color_func(c['NOTIFICATION']['source_nick'])(
 852 |         ' @' + source['screen_name'])
 853 |     notify = color_func(c['NOTIFICATION']['notify'])(
 854 |         'subscribed to your list')
 855 |     date = parser.parse(created_at)
 856 |     clock = fallback_humanize(date)
 857 |     clock = color_func(c['NOTIFICATION']['clock'])(clock)
 858 |     meta = c['NOTIFY_FORMAT']
 859 |     meta = source_user.join(meta.split('#source_user'))
 860 |     meta = notify.join(meta.split('#notify'))
 861 |     meta = clock.join(meta.split('#clock'))
 862 |     meta = emojize(meta)
 863 |     # Output
 864 |     printNicely('')
 865 |     printNicely(meta)
 866 |     print_list(target_object, noti=True)
 867 | 
 868 | 
 869 | def notify_list_user_unsubscribed(e):
 870 |     """
 871 |     Notify a list_user_unsubscribed event
 872 |     """
 873 |     # Retrieve info
 874 |     target = e['target']
 875 |     if target['screen_name'] != c['original_name']:
 876 |         return
 877 |     source = e['source']
 878 |     target_object = [e['target_object']]  # list of Twitter list
 879 |     created_at = e['created_at']
 880 |     # Format
 881 |     source_user = cycle_color(source['name']) + \
 882 |         color_func(c['NOTIFICATION']['source_nick'])(
 883 |         ' @' + source['screen_name'])
 884 |     notify = color_func(c['NOTIFICATION']['notify'])(
 885 |         'unsubscribed from your list')
 886 |     date = parser.parse(created_at)
 887 |     clock = fallback_humanize(date)
 888 |     clock = color_func(c['NOTIFICATION']['clock'])(clock)
 889 |     meta = c['NOTIFY_FORMAT']
 890 |     meta = source_user.join(meta.split('#source_user'))
 891 |     meta = notify.join(meta.split('#notify'))
 892 |     meta = clock.join(meta.split('#clock'))
 893 |     meta = emojize(meta)
 894 |     # Output
 895 |     printNicely('')
 896 |     printNicely(meta)
 897 |     print_list(target_object, noti=True)
 898 | 
 899 | 
 900 | def print_event(e):
 901 |     """
 902 |     Notify an event
 903 |     """
 904 |     event_dict = {
 905 |         'retweet': notify_retweet,
 906 |         'favorite': notify_favorite,
 907 |         'unfavorite': notify_unfavorite,
 908 |         'follow': notify_follow,
 909 |         'list_member_added': notify_list_member_added,
 910 |         'list_member_removed': notify_list_member_removed,
 911 |         'list_user_subscribed': notify_list_user_subscribed,
 912 |         'list_user_unsubscribed': notify_list_user_unsubscribed,
 913 |     }
 914 |     event_dict.get(e['event'], lambda *args: None)(e)
 915 | 
 916 | 
 917 | def show_profile(u):
 918 |     """
 919 |     Show a profile
 920 |     """
 921 |     # Retrieve info
 922 |     name = u['name']
 923 |     screen_name = u['screen_name']
 924 |     description = u['description']
 925 |     profile_image_url = u['profile_image_url']
 926 |     location = u['location']
 927 |     url = u['url']
 928 |     created_at = u['created_at']
 929 |     statuses_count = u['statuses_count']
 930 |     friends_count = u['friends_count']
 931 |     followers_count = u['followers_count']
 932 | 
 933 |     # Create content
 934 |     statuses_count = color_func(
 935 |         c['PROFILE']['statuses_count'])(
 936 |         str(statuses_count) +
 937 |         ' tweets')
 938 |     friends_count = color_func(
 939 |         c['PROFILE']['friends_count'])(
 940 |         str(friends_count) +
 941 |         ' following')
 942 |     followers_count = color_func(
 943 |         c['PROFILE']['followers_count'])(
 944 |         str(followers_count) +
 945 |         ' followers')
 946 |     count = statuses_count + '  ' + friends_count + '  ' + followers_count
 947 |     user = cycle_color(
 948 |         name) + color_func(c['PROFILE']['nick'])(' @' + screen_name + ' : ') + count
 949 |     profile_image_raw_url = 'Profile photo: ' + \
 950 |         color_func(c['PROFILE']['profile_image_url'])(profile_image_url)
 951 |     description = ''.join(
 952 |         lmap(lambda x: x + ' ' * 4 if x == '\n' else x, description))
 953 |     description = color_func(c['PROFILE']['description'])(description)
 954 |     location = 'Location : ' + color_func(c['PROFILE']['location'])(location)
 955 |     url = 'URL : ' + (color_func(c['PROFILE']['url'])(url) if url else '')
 956 |     date = parser.parse(created_at)
 957 |     clock = fallback_humanize(date)
 958 |     clock = 'Joined ' + color_func(c['PROFILE']['clock'])(clock)
 959 | 
 960 |     # Format
 961 |     line1 = u"{u:>{uw}}".format(
 962 |         u=user,
 963 |         uw=len(user) + 2,
 964 |     )
 965 |     line2 = u"{p:>{pw}}".format(
 966 |         p=profile_image_raw_url,
 967 |         pw=len(profile_image_raw_url) + 4,
 968 |     )
 969 |     line3 = u"{d:>{dw}}".format(
 970 |         d=description,
 971 |         dw=len(description) + 4,
 972 |     )
 973 |     line4 = u"{l:>{lw}}".format(
 974 |         l=location,
 975 |         lw=len(location) + 4,
 976 |     )
 977 |     line5 = u"{u:>{uw}}".format(
 978 |         u=url,
 979 |         uw=len(url) + 4,
 980 |     )
 981 |     line6 = u"{c:>{cw}}".format(
 982 |         c=clock,
 983 |         cw=len(clock) + 4,
 984 |     )
 985 | 
 986 |     # Display
 987 |     printNicely('')
 988 |     printNicely(line1)
 989 |     if c['IMAGE_ON_TERM']:
 990 |         try:
 991 |             response = requests.get(profile_image_url)
 992 |             image_to_display(BytesIO(response.content))
 993 |         except:
 994 |             pass
 995 |     else:
 996 |         printNicely(line2)
 997 |     for line in [line3, line4, line5, line6]:
 998 |         printNicely(line)
 999 |     printNicely('')
1000 | 
1001 | 
1002 | def print_trends(trends):
1003 |     """
1004 |     Display topics
1005 |     """
1006 |     for topic in trends[:c['TREND_MAX']]:
1007 |         name = topic['name']
1008 |         url = topic['url']
1009 |         line = cycle_color(name) + ': ' + color_func(c['TREND']['url'])(url)
1010 |         printNicely(line)
1011 |     printNicely('')
1012 | 
1013 | 
1014 | def print_list(group, noti=False):
1015 |     """
1016 |     Display a list
1017 |     """
1018 |     for grp in group:
1019 |         # Format
1020 |         name = grp['full_name']
1021 |         name = color_func(c['GROUP']['name'])(name + ' : ')
1022 |         member = str(grp['member_count'])
1023 |         member = color_func(c['GROUP']['member'])(member + ' member')
1024 |         subscriber = str(grp['subscriber_count'])
1025 |         subscriber = color_func(
1026 |             c['GROUP']['subscriber'])(
1027 |             subscriber +
1028 |             ' subscriber')
1029 |         description = grp['description'].strip()
1030 |         description = color_func(c['GROUP']['description'])(description)
1031 |         mode = grp['mode']
1032 |         mode = color_func(c['GROUP']['mode'])('Type: ' + mode)
1033 |         created_at = grp['created_at']
1034 |         date = parser.parse(created_at)
1035 |         clock = fallback_humanize(date)
1036 |         clock = 'Created at ' + color_func(c['GROUP']['clock'])(clock)
1037 | 
1038 |         prefix = ' ' * 2
1039 |         # Add spaces in begining of line if this is inside a notification
1040 |         if noti:
1041 |             prefix = ' ' * 2 + prefix
1042 |         # Create lines
1043 |         line1 = prefix + name + member + '  ' + subscriber
1044 |         line2 = prefix + ' ' * 2 + description
1045 |         line3 = prefix + ' ' * 2 + mode
1046 |         line4 = prefix + ' ' * 2 + clock
1047 | 
1048 |         # Display
1049 |         printNicely('')
1050 |         printNicely(line1)
1051 |         printNicely(line2)
1052 |         printNicely(line3)
1053 |         printNicely(line4)
1054 | 
1055 |     if not noti:
1056 |         printNicely('')
1057 | 
1058 | 
1059 | def show_calendar(month, date, rel):
1060 |     """
1061 |     Show the calendar in rainbow mode
1062 |     """
1063 |     month = random_rainbow(month)
1064 |     date = ' '.join([cycle_color(i) for i in date.split(' ')])
1065 |     today = str(int(os.popen('date +\'%d\'').read().strip()))
1066 |     # Display
1067 |     printNicely(month)
1068 |     printNicely(date)
1069 |     for line in rel:
1070 |         ary = line.split(' ')
1071 |         ary = lmap(
1072 |             lambda x: color_func(c['CAL']['today'])(x)
1073 |             if x == today
1074 |             else color_func(c['CAL']['days'])(x),
1075 |             ary)
1076 |         printNicely(' '.join(ary))
1077 | 
1078 | 
1079 | def format_quote(tweet):
1080 |     """
1081 |     Quoting format
1082 |     """
1083 |     # Retrieve info
1084 |     screen_name = tweet['user']['screen_name']
1085 |     text = get_full_text(tweet)
1086 |     tid         = str( tweet['id'] )
1087 | 
1088 |     # Validate quote format
1089 |     if '#owner' not in c['QUOTE_FORMAT']:
1090 |         printNicely(light_magenta('Quote should contains #owner'))
1091 |         return False
1092 |     if '#comment' not in c['QUOTE_FORMAT']:
1093 |         printNicely(light_magenta('Quote format should have #comment'))
1094 |         return False
1095 | 
1096 |     # Build formater
1097 |     formater = ''
1098 |     try:
1099 |         formater = c['QUOTE_FORMAT']
1100 | 
1101 |         formater = formater.replace('#owner', screen_name)
1102 |         formater = formater.replace('#tweet', text)
1103 |         formater = formater.replace('#tid',   tid)
1104 | 
1105 |         formater = emojize(formater)
1106 |     except:
1107 |         pass
1108 |     # Highlight like a tweet
1109 |     notice = formater.split()
1110 |     notice = lmap(
1111 |         lambda x: light_green(x)
1112 |         if x == '#comment'
1113 |         else x,
1114 |         notice)
1115 |     notice = lmap(
1116 |         lambda x: color_func(c['TWEET']['rt'])(x)
1117 |         if x == 'RT'
1118 |         else x,
1119 |         notice)
1120 |     notice = lmap(lambda x: cycle_color(x) if x[0] == '@' else x, notice)
1121 |     notice = lmap(
1122 |         lambda x: color_func(c['TWEET']['link'])(x)
1123 |         if x[0:4] == 'http'
1124 |         else x,
1125 |         notice)
1126 |     notice = lmap(
1127 |         lambda x: color_func(c['TWEET']['hashtag'])(x)
1128 |         if x.startswith('#')
1129 |         else x,
1130 |         notice)
1131 |     notice = ' '.join(notice)
1132 |     # Notice
1133 |     notice = light_magenta('Quoting: "') + notice + light_magenta('"')
1134 |     printNicely(notice)
1135 |     return formater
1136 | 
1137 | 
1138 | # Start the color cycle
1139 | start_cycle()
1140 | 


--------------------------------------------------------------------------------
/rainbowstream/emoji.py:
--------------------------------------------------------------------------------
  1 | # This file is based on emoji (https:://github.com/kyokomi/emoji).
  2 | # and (https://github.com/carpedm20/emoji)
  3 | #
  4 | # The MIT License (MIT)
  5 | #
  6 | # Copyright (c) 2014 kyokomi
  7 | #
  8 | # Permission is hereby granted, free of charge, to any person obtaining a copy
  9 | # of this software and associated documentation files (the "Software"), to deal
 10 | # in the Software without restriction, including without limitation the rights
 11 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 12 | # copies of the Software, and to permit persons to whom the Software is
 13 | # furnished to do so, subject to the following conditions::
 14 | #
 15 | # The above copyright notice and this permission notice shall be included in all
 16 | # copies or substantial portions of the Software.
 17 | #
 18 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 19 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 20 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 21 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 22 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 23 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 24 | # SOFTWARE.
 25 | 
 26 | import re
 27 | 
 28 | emojiCodeDict = {
 29 |     "::capricorn::": u"\U00002651",
 30 |     "::end::": u"\U0001f51a",
 31 |     "::no_mobile_phones::": u"\U0001f4f5",
 32 |     "::couple::": u"\U0001f46b",
 33 |     "::snowman::": u"\U000026c4",
 34 |     "::sunrise_over_mountains::": u"\U0001f304",
 35 |     "::suspension_railway::": u"\U0001f69f",
 36 |     "::arrows_counterclockwise::": u"\U0001f504",
 37 |     "::bug::": u"\U0001f41b",
 38 |     "::confused::": u"\U0001f615",
 39 |     "::dress::": u"\U0001f457",
 40 |     "::honeybee::": u"\U0001f41d",
 41 |     "::waning_crescent_moon::": u"\U0001f318",
 42 |     "::balloon::": u"\U0001f388",
 43 |     "::bus::": u"\U0001f68c",
 44 |     "::package::": u"\U0001f4e6",
 45 |     "::pencil2::": u"\U0000270f",
 46 |     "::rage::": u"\U0001f621",
 47 |     "::space_invader::": u"\U0001f47e",
 48 |     "::white_medium_small_square::": u"\U000025fd",
 49 |     "::fast_forward::": u"\U000023e9",
 50 |     "::rice_cracker::": u"\U0001f358",
 51 |     "::incoming_envelope::": u"\U0001f4e8",
 52 |     "::sa::": u"\U0001f202",
 53 |     "::womens::": u"\U0001f6ba",
 54 |     "::arrow_right::": u"\U000027a1",
 55 |     "::construction_worker::": u"\U0001f477",
 56 |     "::notes::": u"\U0001f3b6",
 57 |     "::goat::": u"\U0001f410",
 58 |     "::grey_question::": u"\U00002754",
 59 |     "::lantern::": u"\U0001f3ee",
 60 |     "::rice_scene::": u"\U0001f391",
 61 |     "::running::": u"\U0001f3c3",
 62 |     "::ferris_wheel::": u"\U0001f3a1",
 63 |     "::musical_score::": u"\U0001f3bc",
 64 |     "::sparkle::": u"\U00002747",
 65 |     "::wink::": u"\U0001f609",
 66 |     "::art::": u"\U0001f3a8",
 67 |     "::clock330::": u"\U0001f55e",
 68 |     "::minidisc::": u"\U0001f4bd",
 69 |     "::no_entry_sign::": u"\U0001f6ab",
 70 |     "::wind_chime::": u"\U0001f390",
 71 |     "::cyclone::": u"\U0001f300",
 72 |     "::herb::": u"\U0001f33f",
 73 |     "::leopard::": u"\U0001f406",
 74 |     "::banana::": u"\U0001f34c",
 75 |     "::handbag::": u"\U0001f45c",
 76 |     "::honey_pot::": u"\U0001f36f",
 77 |     "::ok::": u"\U0001f197",
 78 |     "::hearts::": u"\U00002665",
 79 |     "::passport_control::": u"\U0001f6c2",
 80 |     "::moyai::": u"\U0001f5ff",
 81 |     "::smile::": u"\U0001f604",
 82 |     "::tiger2::": u"\U0001f405",
 83 |     "::twisted_rightwards_arrows::": u"\U0001f500",
 84 |     "::children_crossing::": u"\U0001f6b8",
 85 |     "::cow::": u"\U0001f42e",
 86 |     "::point_up::": u"\U0000261d",
 87 |     "::house::": u"\U0001f3e0",
 88 |     "::man_with_turban::": u"\U0001f473",
 89 |     "::mountain_railway::": u"\U0001f69e",
 90 |     "::vibration_mode::": u"\U0001f4f3",
 91 |     "::blowfish::": u"\U0001f421",
 92 |     "::it::": u"\U0001f1ee\U0001f1f9",
 93 |     "::oden::": u"\U0001f362",
 94 |     "::clock3::": u"\U0001f552",
 95 |     "::lollipop::": u"\U0001f36d",
 96 |     "::train::": u"\U0001f68b",
 97 |     "::scissors::": u"\U00002702",
 98 |     "::triangular_ruler::": u"\U0001f4d0",
 99 |     "::wedding::": u"\U0001f492",
100 |     "::flashlight::": u"\U0001f526",
101 |     "::secret::": u"\U00003299",
102 |     "::sushi::": u"\U0001f363",
103 |     "::blue_car::": u"\U0001f699",
104 |     "::cd::": u"\U0001f4bf",
105 |     "::milky_way::": u"\U0001f30c",
106 |     "::mortar_board::": u"\U0001f393",
107 |     "::crown::": u"\U0001f451",
108 |     "::speech_balloon::": u"\U0001f4ac",
109 |     "::bento::": u"\U0001f371",
110 |     "::grey_exclamation::": u"\U00002755",
111 |     "::hotel::": u"\U0001f3e8",
112 |     "::keycap_ten::": u"\U0001f51f",
113 |     "::newspaper::": u"\U0001f4f0",
114 |     "::outbox_tray::": u"\U0001f4e4",
115 |     "::racehorse::": u"\U0001f40e",
116 |     "::laughing::": u"\U0001f606",
117 |     "::black_large_square::": u"\U00002b1b",
118 |     "::books::": u"\U0001f4da",
119 |     "::eight_spoked_asterisk::": u"\U00002733",
120 |     "::heavy_check_mark::": u"\U00002714",
121 |     "::m::": u"\U000024c2",
122 |     "::wave::": u"\U0001f44b",
123 |     "::bicyclist::": u"\U0001f6b4",
124 |     "::cocktail::": u"\U0001f378",
125 |     "::european_castle::": u"\U0001f3f0",
126 |     "::point_down::": u"\U0001f447",
127 |     "::tokyo_tower::": u"\U0001f5fc",
128 |     "::battery::": u"\U0001f50b",
129 |     "::dancer::": u"\U0001f483",
130 |     "::repeat::": u"\U0001f501",
131 |     "::ru::": u"\U0001f1f7\U0001f1fa",
132 |     "::new_moon::": u"\U0001f311",
133 |     "::church::": u"\U000026ea",
134 |     "::date::": u"\U0001f4c5",
135 |     "::earth_americas::": u"\U0001f30e",
136 |     "::footprints::": u"\U0001f463",
137 |     "::libra::": u"\U0000264e",
138 |     "::mountain_cableway::": u"\U0001f6a0",
139 |     "::small_red_triangle_down::": u"\U0001f53b",
140 |     "::top::": u"\U0001f51d",
141 |     "::sunglasses::": u"\U0001f60e",
142 |     "::abcd::": u"\U0001f521",
143 |     "::cl::": u"\U0001f191",
144 |     "::ski::": u"\U0001f3bf",
145 |     "::book::": u"\U0001f4d6",
146 |     "::hourglass_flowing_sand::": u"\U000023f3",
147 |     "::stuck_out_tongue_closed_eyes::": u"\U0001f61d",
148 |     "::cold_sweat::": u"\U0001f630",
149 |     "::headphones::": u"\U0001f3a7",
150 |     "::confetti_ball::": u"\U0001f38a",
151 |     "::gemini::": u"\U0000264a",
152 |     "::new::": u"\U0001f195",
153 |     "::pray::": u"\U0001f64f",
154 |     "::watch::": u"\U0000231a",
155 |     "::coffee::": u"\U00002615",
156 |     "::ghost::": u"\U0001f47b",
157 |     "::on::": u"\U0001f51b",
158 |     "::pouch::": u"\U0001f45d",
159 |     "::taxi::": u"\U0001f695",
160 |     "::hocho::": u"\U0001f52a",
161 |     "::yum::": u"\U0001f60b",
162 |     "::heavy_plus_sign::": u"\U00002795",
163 |     "::tada::": u"\U0001f389",
164 |     "::arrow_heading_down::": u"\U00002935",
165 |     "::clock530::": u"\U0001f560",
166 |     "::poultry_leg::": u"\U0001f357",
167 |     "::elephant::": u"\U0001f418",
168 |     "::gb::": u"\U0001f1ec\U0001f1e7",
169 |     "::mahjong::": u"\U0001f004",
170 |     "::rice::": u"\U0001f35a",
171 |     "::musical_note::": u"\U0001f3b5",
172 |     "::beginner::": u"\U0001f530",
173 |     "::small_red_triangle::": u"\U0001f53a",
174 |     "::tomato::": u"\U0001f345",
175 |     "::clock1130::": u"\U0001f566",
176 |     "::japanese_castle::": u"\U0001f3ef",
177 |     "::sun_with_face::": u"\U0001f31e",
178 |     "::four::": u"\U00000034\U000020e3",
179 |     "::microphone::": u"\U0001f3a4",
180 |     "::tennis::": u"\U0001f3be",
181 |     "::arrow_up_down::": u"\U00002195",
182 |     "::cn::": u"\U0001f1e8\U0001f1f3",
183 |     "::horse_racing::": u"\U0001f3c7",
184 |     "::no_bicycles::": u"\U0001f6b3",
185 |     "::snail::": u"\U0001f40c",
186 |     "::free::": u"\U0001f193",
187 |     "::beetle::": u"\U0001f41e",
188 |     "::black_small_square::": u"\U000025aa",
189 |     "::file_folder::": u"\U0001f4c1",
190 |     "::hushed::": u"\U0001f62f",
191 |     "::skull::": u"\U0001f480",
192 |     "::ab::": u"\U0001f18e",
193 |     "::rocket::": u"\U0001f680",
194 |     "::sweet_potato::": u"\U0001f360",
195 |     "::guitar::": u"\U0001f3b8",
196 |     "::poodle::": u"\U0001f429",
197 |     "::tulip::": u"\U0001f337",
198 |     "::large_orange_diamond::": u"\U0001f536",
199 |     "::-1::": u"\U0001f44e",
200 |     "::chart_with_upwards_trend::": u"\U0001f4c8",
201 |     "::de::": u"\U0001f1e9\U0001f1ea",
202 |     "::grapes::": u"\U0001f347",
203 |     "::ideograph_advantage::": u"\U0001f250",
204 |     "::japanese_ogre::": u"\U0001f479",
205 |     "::telephone::": u"\U0000260e",
206 |     "::clock230::": u"\U0001f55d",
207 |     "::hourglass::": u"\U0000231b",
208 |     "::leftwards_arrow_with_hook::": u"\U000021a9",
209 |     "::sparkler::": u"\U0001f387",
210 |     "::black_joker::": u"\U0001f0cf",
211 |     "::clock730::": u"\U0001f562",
212 |     "::first_quarter_moon_with_face::": u"\U0001f31b",
213 |     "::man::": u"\U0001f468",
214 |     "::clock4::": u"\U0001f553",
215 |     "::fishing_pole_and_fish::": u"\U0001f3a3",
216 |     "::tophat::": u"\U0001f3a9",
217 |     "::white_medium_square::": u"\U000025fb",
218 |     "::mega::": u"\U0001f4e3",
219 |     "::spaghetti::": u"\U0001f35d",
220 |     "::dart::": u"\U0001f3af",
221 |     "::girl::": u"\U0001f467",
222 |     "::womans_hat::": u"\U0001f452",
223 |     "::bullettrain_front::": u"\U0001f685",
224 |     "::department_store::": u"\U0001f3ec",
225 |     "::heartbeat::": u"\U0001f493",
226 |     "::palm_tree::": u"\U0001f334",
227 |     "::swimmer::": u"\U0001f3ca",
228 |     "::yellow_heart::": u"\U0001f49b",
229 |     "::arrow_upper_right::": u"\U00002197",
230 |     "::clock2::": u"\U0001f551",
231 |     "::high_heel::": u"\U0001f460",
232 |     "::arrow_double_up::": u"\U000023eb",
233 |     "::cry::": u"\U0001f622",
234 |     "::dvd::": u"\U0001f4c0",
235 |     "::e-mail::": u"\U0001f4e7",
236 |     "::baby_bottle::": u"\U0001f37c",
237 |     "::cool::": u"\U0001f192",
238 |     "::floppy_disk::": u"\U0001f4be",
239 |     "::iphone::": u"\U0001f4f1",
240 |     "::minibus::": u"\U0001f690",
241 |     "::rooster::": u"\U0001f413",
242 |     "::three::": u"\U00000033\U000020e3",
243 |     "::white_small_square::": u"\U000025ab",
244 |     "::cancer::": u"\U0000264b",
245 |     "::question::": u"\U00002753",
246 |     "::sake::": u"\U0001f376",
247 |     "::birthday::": u"\U0001f382",
248 |     "::dog2::": u"\U0001f415",
249 |     "::loudspeaker::": u"\U0001f4e2",
250 |     "::arrow_up_small::": u"\U0001f53c",
251 |     "::camel::": u"\U0001f42b",
252 |     "::koala::": u"\U0001f428",
253 |     "::mag_right::": u"\U0001f50e",
254 |     "::soccer::": u"\U000026bd",
255 |     "::bike::": u"\U0001f6b2",
256 |     "::ear_of_rice::": u"\U0001f33e",
257 |     "::shit::": u"\U0001f4a9",
258 |     "::u7981::": u"\U0001f232",
259 |     "::bath::": u"\U0001f6c0",
260 |     "::baby::": u"\U0001f476",
261 |     "::lock_with_ink_pen::": u"\U0001f50f",
262 |     "::necktie::": u"\U0001f454",
263 |     "::bikini::": u"\U0001f459",
264 |     "::blush::": u"\U0001f60a",
265 |     "::heartpulse::": u"\U0001f497",
266 |     "::pig_nose::": u"\U0001f43d",
267 |     "::straight_ruler::": u"\U0001f4cf",
268 |     "::u6e80::": u"\U0001f235",
269 |     "::gift::": u"\U0001f381",
270 |     "::traffic_light::": u"\U0001f6a5",
271 |     "::hibiscus::": u"\U0001f33a",
272 |     "::couple_with_heart::": u"\U0001f491",
273 |     "::pushpin::": u"\U0001f4cc",
274 |     "::u6709::": u"\U0001f236",
275 |     "::walking::": u"\U0001f6b6",
276 |     "::grinning::": u"\U0001f600",
277 |     "::hash::": u"\U00000023\U000020e3",
278 |     "::radio_button::": u"\U0001f518",
279 |     "::raised_hand::": u"\U0000270b",
280 |     "::shaved_ice::": u"\U0001f367",
281 |     "::barber::": u"\U0001f488",
282 |     "::cat::": u"\U0001f431",
283 |     "::heavy_exclamation_mark::": u"\U00002757",
284 |     "::ice_cream::": u"\U0001f368",
285 |     "::mask::": u"\U0001f637",
286 |     "::pig2::": u"\U0001f416",
287 |     "::triangular_flag_on_post::": u"\U0001f6a9",
288 |     "::arrow_upper_left::": u"\U00002196",
289 |     "::bee::": u"\U0001f41d",
290 |     "::beer::": u"\U0001f37a",
291 |     "::black_nib::": u"\U00002712",
292 |     "::exclamation::": u"\U00002757",
293 |     "::dog::": u"\U0001f436",
294 |     "::fire::": u"\U0001f525",
295 |     "::ant::": u"\U0001f41c",
296 |     "::broken_heart::": u"\U0001f494",
297 |     "::chart::": u"\U0001f4b9",
298 |     "::clock1::": u"\U0001f550",
299 |     "::bomb::": u"\U0001f4a3",
300 |     "::virgo::": u"\U0000264d",
301 |     "::a::": u"\U0001f170",
302 |     "::fork_and_knife::": u"\U0001f374",
303 |     "::copyright::": u"\U000000a9",
304 |     "::curly_loop::": u"\U000027b0",
305 |     "::full_moon::": u"\U0001f315",
306 |     "::shoe::": u"\U0001f45e",
307 |     "::european_post_office::": u"\U0001f3e4",
308 |     "::ng::": u"\U0001f196",
309 |     "::office::": u"\U0001f3e2",
310 |     "::raising_hand::": u"\U0001f64b",
311 |     "::revolving_hearts::": u"\U0001f49e",
312 |     "::aquarius::": u"\U00002652",
313 |     "::electric_plug::": u"\U0001f50c",
314 |     "::meat_on_bone::": u"\U0001f356",
315 |     "::mens::": u"\U0001f6b9",
316 |     "::briefcase::": u"\U0001f4bc",
317 |     "::ship::": u"\U0001f6a2",
318 |     "::anchor::": u"\U00002693",
319 |     "::ballot_box_with_check::": u"\U00002611",
320 |     "::bear::": u"\U0001f43b",
321 |     "::beers::": u"\U0001f37b",
322 |     "::dromedary_camel::": u"\U0001f42a",
323 |     "::nut_and_bolt::": u"\U0001f529",
324 |     "::construction::": u"\U0001f6a7",
325 |     "::golf::": u"\U000026f3",
326 |     "::toilet::": u"\U0001f6bd",
327 |     "::blue_book::": u"\U0001f4d8",
328 |     "::boom::": u"\U0001f4a5",
329 |     "::deciduous_tree::": u"\U0001f333",
330 |     "::kissing_closed_eyes::": u"\U0001f61a",
331 |     "::smiley_cat::": u"\U0001f63a",
332 |     "::fuelpump::": u"\U000026fd",
333 |     "::kiss::": u"\U0001f48b",
334 |     "::clock10::": u"\U0001f559",
335 |     "::sheep::": u"\U0001f411",
336 |     "::white_flower::": u"\U0001f4ae",
337 |     "::boar::": u"\U0001f417",
338 |     "::currency_exchange::": u"\U0001f4b1",
339 |     "::facepunch::": u"\U0001f44a",
340 |     "::flower_playing_cards::": u"\U0001f3b4",
341 |     "::person_frowning::": u"\U0001f64d",
342 |     "::poop::": u"\U0001f4a9",
343 |     "::satisfied::": u"\U0001f606",
344 |     "::8ball::": u"\U0001f3b1",
345 |     "::disappointed_relieved::": u"\U0001f625",
346 |     "::panda_face::": u"\U0001f43c",
347 |     "::ticket::": u"\U0001f3ab",
348 |     "::us::": u"\U0001f1fa\U0001f1f8",
349 |     "::waxing_crescent_moon::": u"\U0001f312",
350 |     "::dragon::": u"\U0001f409",
351 |     "::gun::": u"\U0001f52b",
352 |     "::mount_fuji::": u"\U0001f5fb",
353 |     "::new_moon_with_face::": u"\U0001f31a",
354 |     "::star2::": u"\U0001f31f",
355 |     "::grimacing::": u"\U0001f62c",
356 |     "::confounded::": u"\U0001f616",
357 |     "::congratulations::": u"\U00003297",
358 |     "::custard::": u"\U0001f36e",
359 |     "::frowning::": u"\U0001f626",
360 |     "::maple_leaf::": u"\U0001f341",
361 |     "::police_car::": u"\U0001f693",
362 |     "::cloud::": u"\U00002601",
363 |     "::jeans::": u"\U0001f456",
364 |     "::fish::": u"\U0001f41f",
365 |     "::wavy_dash::": u"\U00003030",
366 |     "::clock5::": u"\U0001f554",
367 |     "::santa::": u"\U0001f385",
368 |     "::japan::": u"\U0001f5fe",
369 |     "::oncoming_taxi::": u"\U0001f696",
370 |     "::whale::": u"\U0001f433",
371 |     "::arrow_forward::": u"\U000025b6",
372 |     "::kissing_heart::": u"\U0001f618",
373 |     "::bullettrain_side::": u"\U0001f684",
374 |     "::fearful::": u"\U0001f628",
375 |     "::moneybag::": u"\U0001f4b0",
376 |     "::runner::": u"\U0001f3c3",
377 |     "::mailbox::": u"\U0001f4eb",
378 |     "::sandal::": u"\U0001f461",
379 |     "::zzz::": u"\U0001f4a4",
380 |     "::apple::": u"\U0001f34e",
381 |     "::arrow_heading_up::": u"\U00002934",
382 |     "::family::": u"\U0001f46a",
383 |     "::heavy_minus_sign::": u"\U00002796",
384 |     "::saxophone::": u"\U0001f3b7",
385 |     "::u5272::": u"\U0001f239",
386 |     "::black_square_button::": u"\U0001f532",
387 |     "::bouquet::": u"\U0001f490",
388 |     "::love_letter::": u"\U0001f48c",
389 |     "::metro::": u"\U0001f687",
390 |     "::small_blue_diamond::": u"\U0001f539",
391 |     "::thought_balloon::": u"\U0001f4ad",
392 |     "::arrow_up::": u"\U00002b06",
393 |     "::no_pedestrians::": u"\U0001f6b7",
394 |     "::smirk::": u"\U0001f60f",
395 |     "::blue_heart::": u"\U0001f499",
396 |     "::large_blue_diamond::": u"\U0001f537",
397 |     "::vs::": u"\U0001f19a",
398 |     "::v::": u"\U0000270c",
399 |     "::wheelchair::": u"\U0000267f",
400 |     "::couplekiss::": u"\U0001f48f",
401 |     "::tent::": u"\U000026fa",
402 |     "::purple_heart::": u"\U0001f49c",
403 |     "::relaxed::": u"\U0000263a",
404 |     "::accept::": u"\U0001f251",
405 |     "::green_heart::": u"\U0001f49a",
406 |     "::pouting_cat::": u"\U0001f63e",
407 |     "::tram::": u"\U0001f68a",
408 |     "::bangbang::": u"\U0000203c",
409 |     "::collision::": u"\U0001f4a5",
410 |     "::convenience_store::": u"\U0001f3ea",
411 |     "::person_with_blond_hair::": u"\U0001f471",
412 |     "::uk::": u"\U0001f1ec\U0001f1e7",
413 |     "::peach::": u"\U0001f351",
414 |     "::tired_face::": u"\U0001f62b",
415 |     "::bread::": u"\U0001f35e",
416 |     "::mailbox_closed::": u"\U0001f4ea",
417 |     "::open_mouth::": u"\U0001f62e",
418 |     "::pig::": u"\U0001f437",
419 |     "::put_litter_in_its_place::": u"\U0001f6ae",
420 |     "::u7a7a::": u"\U0001f233",
421 |     "::bulb::": u"\U0001f4a1",
422 |     "::clock9::": u"\U0001f558",
423 |     "::envelope_with_arrow::": u"\U0001f4e9",
424 |     "::pisces::": u"\U00002653",
425 |     "::baggage_claim::": u"\U0001f6c4",
426 |     "::egg::": u"\U0001f373",
427 |     "::sweat_smile::": u"\U0001f605",
428 |     "::boat::": u"\U000026f5",
429 |     "::fr::": u"\U0001f1eb\U0001f1f7",
430 |     "::heavy_division_sign::": u"\U00002797",
431 |     "::muscle::": u"\U0001f4aa",
432 |     "::paw_prints::": u"\U0001f43e",
433 |     "::arrow_left::": u"\U00002b05",
434 |     "::black_circle::": u"\U000026ab",
435 |     "::kissing_smiling_eyes::": u"\U0001f619",
436 |     "::star::": u"\U00002b50",
437 |     "::steam_locomotive::": u"\U0001f682",
438 |     "::1234::": u"\U0001f522",
439 |     "::clock130::": u"\U0001f55c",
440 |     "::kr::": u"\U0001f1f0\U0001f1f7",
441 |     "::monorail::": u"\U0001f69d",
442 |     "::school::": u"\U0001f3eb",
443 |     "::seven::": u"\U00000037\U000020e3",
444 |     "::baby_chick::": u"\U0001f424",
445 |     "::bridge_at_night::": u"\U0001f309",
446 |     "::hotsprings::": u"\U00002668",
447 |     "::rose::": u"\U0001f339",
448 |     "::love_hotel::": u"\U0001f3e9",
449 |     "::princess::": u"\U0001f478",
450 |     "::ramen::": u"\U0001f35c",
451 |     "::scroll::": u"\U0001f4dc",
452 |     "::tropical_fish::": u"\U0001f420",
453 |     "::heart_eyes_cat::": u"\U0001f63b",
454 |     "::information_desk_person::": u"\U0001f481",
455 |     "::mouse::": u"\U0001f42d",
456 |     "::no_smoking::": u"\U0001f6ad",
457 |     "::post_office::": u"\U0001f3e3",
458 |     "::stars::": u"\U0001f320",
459 |     "::arrow_double_down::": u"\U000023ec",
460 |     "::unlock::": u"\U0001f513",
461 |     "::arrow_backward::": u"\U000025c0",
462 |     "::hand::": u"\U0000270b",
463 |     "::hospital::": u"\U0001f3e5",
464 |     "::ocean::": u"\U0001f30a",
465 |     "::mountain_bicyclist::": u"\U0001f6b5",
466 |     "::octopus::": u"\U0001f419",
467 |     "::sos::": u"\U0001f198",
468 |     "::dizzy_face::": u"\U0001f635",
469 |     "::tongue::": u"\U0001f445",
470 |     "::train2::": u"\U0001f686",
471 |     "::checkered_flag::": u"\U0001f3c1",
472 |     "::orange_book::": u"\U0001f4d9",
473 |     "::sound::": u"\U0001f509",
474 |     "::aerial_tramway::": u"\U0001f6a1",
475 |     "::bell::": u"\U0001f514",
476 |     "::dragon_face::": u"\U0001f432",
477 |     "::flipper::": u"\U0001f42c",
478 |     "::ok_woman::": u"\U0001f646",
479 |     "::performing_arts::": u"\U0001f3ad",
480 |     "::postal_horn::": u"\U0001f4ef",
481 |     "::clock1030::": u"\U0001f565",
482 |     "::email::": u"\U00002709",
483 |     "::green_book::": u"\U0001f4d7",
484 |     "::point_up_2::": u"\U0001f446",
485 |     "::high_brightness::": u"\U0001f506",
486 |     "::running_shirt_with_sash::": u"\U0001f3bd",
487 |     "::bookmark::": u"\U0001f516",
488 |     "::sob::": u"\U0001f62d",
489 |     "::arrow_lower_right::": u"\U00002198",
490 |     "::point_left::": u"\U0001f448",
491 |     "::purse::": u"\U0001f45b",
492 |     "::sparkles::": u"\U00002728",
493 |     "::black_medium_small_square::": u"\U000025fe",
494 |     "::pound::": u"\U0001f4b7",
495 |     "::rabbit::": u"\U0001f430",
496 |     "::woman::": u"\U0001f469",
497 |     "::negative_squared_cross_mark::": u"\U0000274e",
498 |     "::open_book::": u"\U0001f4d6",
499 |     "::smiling_imp::": u"\U0001f608",
500 |     "::spades::": u"\U00002660",
501 |     "::baseball::": u"\U000026be",
502 |     "::fountain::": u"\U000026f2",
503 |     "::joy::": u"\U0001f602",
504 |     "::lipstick::": u"\U0001f484",
505 |     "::partly_sunny::": u"\U000026c5",
506 |     "::ram::": u"\U0001f40f",
507 |     "::red_circle::": u"\U0001f534",
508 |     "::cop::": u"\U0001f46e",
509 |     "::green_apple::": u"\U0001f34f",
510 |     "::registered::": u"\U000000ae",
511 |     "::+1::": u"\U0001f44d",
512 |     "::crying_cat_face::": u"\U0001f63f",
513 |     "::innocent::": u"\U0001f607",
514 |     "::mobile_phone_off::": u"\U0001f4f4",
515 |     "::underage::": u"\U0001f51e",
516 |     "::dolphin::": u"\U0001f42c",
517 |     "::busts_in_silhouette::": u"\U0001f465",
518 |     "::umbrella::": u"\U00002614",
519 |     "::angel::": u"\U0001f47c",
520 |     "::small_orange_diamond::": u"\U0001f538",
521 |     "::sunflower::": u"\U0001f33b",
522 |     "::link::": u"\U0001f517",
523 |     "::notebook::": u"\U0001f4d3",
524 |     "::oncoming_bus::": u"\U0001f68d",
525 |     "::bookmark_tabs::": u"\U0001f4d1",
526 |     "::calendar::": u"\U0001f4c6",
527 |     "::izakaya_lantern::": u"\U0001f3ee",
528 |     "::mans_shoe::": u"\U0001f45e",
529 |     "::name_badge::": u"\U0001f4db",
530 |     "::closed_lock_with_key::": u"\U0001f510",
531 |     "::fist::": u"\U0000270a",
532 |     "::id::": u"\U0001f194",
533 |     "::ambulance::": u"\U0001f691",
534 |     "::musical_keyboard::": u"\U0001f3b9",
535 |     "::ribbon::": u"\U0001f380",
536 |     "::seedling::": u"\U0001f331",
537 |     "::tv::": u"\U0001f4fa",
538 |     "::football::": u"\U0001f3c8",
539 |     "::nail_care::": u"\U0001f485",
540 |     "::seat::": u"\U0001f4ba",
541 |     "::alarm_clock::": u"\U000023f0",
542 |     "::money_with_wings::": u"\U0001f4b8",
543 |     "::relieved::": u"\U0001f60c",
544 |     "::womans_clothes::": u"\U0001f45a",
545 |     "::lips::": u"\U0001f444",
546 |     "::clubs::": u"\U00002663",
547 |     "::house_with_garden::": u"\U0001f3e1",
548 |     "::sunrise::": u"\U0001f305",
549 |     "::monkey::": u"\U0001f412",
550 |     "::six::": u"\U00000036\U000020e3",
551 |     "::smiley::": u"\U0001f603",
552 |     "::feet::": u"\U0001f43e",
553 |     "::waning_gibbous_moon::": u"\U0001f316",
554 |     "::yen::": u"\U0001f4b4",
555 |     "::baby_symbol::": u"\U0001f6bc",
556 |     "::signal_strength::": u"\U0001f4f6",
557 |     "::boy::": u"\U0001f466",
558 |     "::busstop::": u"\U0001f68f",
559 |     "::computer::": u"\U0001f4bb",
560 |     "::night_with_stars::": u"\U0001f303",
561 |     "::older_woman::": u"\U0001f475",
562 |     "::parking::": u"\U0001f17f",
563 |     "::trumpet::": u"\U0001f3ba",
564 |     "::100::": u"\U0001f4af",
565 |     "::sweat_drops::": u"\U0001f4a6",
566 |     "::wc::": u"\U0001f6be",
567 |     "::b::": u"\U0001f171",
568 |     "::cupid::": u"\U0001f498",
569 |     "::five::": u"\U00000035\U000020e3",
570 |     "::part_alternation_mark::": u"\U0000303d",
571 |     "::snowboarder::": u"\U0001f3c2",
572 |     "::warning::": u"\U000026a0",
573 |     "::white_large_square::": u"\U00002b1c",
574 |     "::zap::": u"\U000026a1",
575 |     "::arrow_down_small::": u"\U0001f53d",
576 |     "::clock430::": u"\U0001f55f",
577 |     "::expressionless::": u"\U0001f611",
578 |     "::phone::": u"\U0000260e",
579 |     "::roller_coaster::": u"\U0001f3a2",
580 |     "::lemon::": u"\U0001f34b",
581 |     "::one::": u"\U00000031\U000020e3",
582 |     "::christmas_tree::": u"\U0001f384",
583 |     "::hankey::": u"\U0001f4a9",
584 |     "::hatched_chick::": u"\U0001f425",
585 |     "::u7533::": u"\U0001f238",
586 |     "::large_blue_circle::": u"\U0001f535",
587 |     "::up::": u"\U0001f199",
588 |     "::wine_glass::": u"\U0001f377",
589 |     "::x::": u"\U0000274c",
590 |     "::nose::": u"\U0001f443",
591 |     "::rewind::": u"\U000023ea",
592 |     "::two_hearts::": u"\U0001f495",
593 |     "::envelope::": u"\U00002709",
594 |     "::oncoming_automobile::": u"\U0001f698",
595 |     "::ophiuchus::": u"\U000026ce",
596 |     "::ring::": u"\U0001f48d",
597 |     "::tropical_drink::": u"\U0001f379",
598 |     "::turtle::": u"\U0001f422",
599 |     "::crescent_moon::": u"\U0001f319",
600 |     "::koko::": u"\U0001f201",
601 |     "::microscope::": u"\U0001f52c",
602 |     "::rugby_football::": u"\U0001f3c9",
603 |     "::smoking::": u"\U0001f6ac",
604 |     "::anger::": u"\U0001f4a2",
605 |     "::aries::": u"\U00002648",
606 |     "::city_sunset::": u"\U0001f306",
607 |     "::clock1230::": u"\U0001f567",
608 |     "::mailbox_with_no_mail::": u"\U0001f4ed",
609 |     "::movie_camera::": u"\U0001f3a5",
610 |     "::pager::": u"\U0001f4df",
611 |     "::zero::": u"\U00000030\U000020e3",
612 |     "::bank::": u"\U0001f3e6",
613 |     "::eight_pointed_black_star::": u"\U00002734",
614 |     "::knife::": u"\U0001f52a",
615 |     "::u7121::": u"\U0001f21a",
616 |     "::customs::": u"\U0001f6c3",
617 |     "::melon::": u"\U0001f348",
618 |     "::rowboat::": u"\U0001f6a3",
619 |     "::corn::": u"\U0001f33d",
620 |     "::eggplant::": u"\U0001f346",
621 |     "::heart_decoration::": u"\U0001f49f",
622 |     "::rotating_light::": u"\U0001f6a8",
623 |     "::round_pushpin::": u"\U0001f4cd",
624 |     "::cat2::": u"\U0001f408",
625 |     "::chocolate_bar::": u"\U0001f36b",
626 |     "::no_bell::": u"\U0001f515",
627 |     "::radio::": u"\U0001f4fb",
628 |     "::droplet::": u"\U0001f4a7",
629 |     "::hamburger::": u"\U0001f354",
630 |     "::fire_engine::": u"\U0001f692",
631 |     "::heart::": u"\U00002764",
632 |     "::potable_water::": u"\U0001f6b0",
633 |     "::telephone_receiver::": u"\U0001f4de",
634 |     "::dash::": u"\U0001f4a8",
635 |     "::globe_with_meridians::": u"\U0001f310",
636 |     "::guardsman::": u"\U0001f482",
637 |     "::heavy_multiplication_x::": u"\U00002716",
638 |     "::chart_with_downwards_trend::": u"\U0001f4c9",
639 |     "::imp::": u"\U0001f47f",
640 |     "::earth_asia::": u"\U0001f30f",
641 |     "::mouse2::": u"\U0001f401",
642 |     "::notebook_with_decorative_cover::": u"\U0001f4d4",
643 |     "::telescope::": u"\U0001f52d",
644 |     "::trolleybus::": u"\U0001f68e",
645 |     "::card_index::": u"\U0001f4c7",
646 |     "::euro::": u"\U0001f4b6",
647 |     "::dollar::": u"\U0001f4b5",
648 |     "::fax::": u"\U0001f4e0",
649 |     "::mailbox_with_mail::": u"\U0001f4ec",
650 |     "::raised_hands::": u"\U0001f64c",
651 |     "::disappointed::": u"\U0001f61e",
652 |     "::foggy::": u"\U0001f301",
653 |     "::person_with_pouting_face::": u"\U0001f64e",
654 |     "::statue_of_liberty::": u"\U0001f5fd",
655 |     "::dolls::": u"\U0001f38e",
656 |     "::light_rail::": u"\U0001f688",
657 |     "::pencil::": u"\U0001f4dd",
658 |     "::speak_no_evil::": u"\U0001f64a",
659 |     "::calling::": u"\U0001f4f2",
660 |     "::clock830::": u"\U0001f563",
661 |     "::cow2::": u"\U0001f404",
662 |     "::hear_no_evil::": u"\U0001f649",
663 |     "::scream_cat::": u"\U0001f640",
664 |     "::smile_cat::": u"\U0001f638",
665 |     "::tractor::": u"\U0001f69c",
666 |     "::clock11::": u"\U0001f55a",
667 |     "::doughnut::": u"\U0001f369",
668 |     "::hammer::": u"\U0001f528",
669 |     "::loop::": u"\U000027bf",
670 |     "::moon::": u"\U0001f314",
671 |     "::soon::": u"\U0001f51c",
672 |     "::cinema::": u"\U0001f3a6",
673 |     "::factory::": u"\U0001f3ed",
674 |     "::flushed::": u"\U0001f633",
675 |     "::mute::": u"\U0001f507",
676 |     "::neutral_face::": u"\U0001f610",
677 |     "::scorpius::": u"\U0000264f",
678 |     "::wolf::": u"\U0001f43a",
679 |     "::clapper::": u"\U0001f3ac",
680 |     "::joy_cat::": u"\U0001f639",
681 |     "::pensive::": u"\U0001f614",
682 |     "::sleeping::": u"\U0001f634",
683 |     "::credit_card::": u"\U0001f4b3",
684 |     "::leo::": u"\U0000264c",
685 |     "::man_with_gua_pi_mao::": u"\U0001f472",
686 |     "::open_hands::": u"\U0001f450",
687 |     "::tea::": u"\U0001f375",
688 |     "::arrow_down::": u"\U00002b07",
689 |     "::nine::": u"\U00000039\U000020e3",
690 |     "::punch::": u"\U0001f44a",
691 |     "::slot_machine::": u"\U0001f3b0",
692 |     "::clap::": u"\U0001f44f",
693 |     "::information_source::": u"\U00002139",
694 |     "::tiger::": u"\U0001f42f",
695 |     "::city_sunrise::": u"\U0001f307",
696 |     "::dango::": u"\U0001f361",
697 |     "::thumbsdown::": u"\U0001f44e",
698 |     "::u6307::": u"\U0001f22f",
699 |     "::curry::": u"\U0001f35b",
700 |     "::cherries::": u"\U0001f352",
701 |     "::clock6::": u"\U0001f555",
702 |     "::clock7::": u"\U0001f556",
703 |     "::older_man::": u"\U0001f474",
704 |     "::oncoming_police_car::": u"\U0001f694",
705 |     "::syringe::": u"\U0001f489",
706 |     "::heavy_dollar_sign::": u"\U0001f4b2",
707 |     "::open_file_folder::": u"\U0001f4c2",
708 |     "::arrow_right_hook::": u"\U000021aa",
709 |     "::articulated_lorry::": u"\U0001f69b",
710 |     "::dancers::": u"\U0001f46f",
711 |     "::kissing_cat::": u"\U0001f63d",
712 |     "::rainbow::": u"\U0001f308",
713 |     "::u5408::": u"\U0001f234",
714 |     "::boot::": u"\U0001f462",
715 |     "::carousel_horse::": u"\U0001f3a0",
716 |     "::fried_shrimp::": u"\U0001f364",
717 |     "::lock::": u"\U0001f512",
718 |     "::non-potable_water::": u"\U0001f6b1",
719 |     "::o::": u"\U00002b55",
720 |     "::persevere::": u"\U0001f623",
721 |     "::diamond_shape_with_a_dot_inside::": u"\U0001f4a0",
722 |     "::fallen_leaf::": u"\U0001f342",
723 |     "::massage::": u"\U0001f486",
724 |     "::volcano::": u"\U0001f30b",
725 |     "::gem::": u"\U0001f48e",
726 |     "::shower::": u"\U0001f6bf",
727 |     "::speaker::": u"\U0001f508",
728 |     "::last_quarter_moon_with_face::": u"\U0001f31c",
729 |     "::mag::": u"\U0001f50d",
730 |     "::anguished::": u"\U0001f627",
731 |     "::monkey_face::": u"\U0001f435",
732 |     "::sunny::": u"\U00002600",
733 |     "::tangerine::": u"\U0001f34a",
734 |     "::point_right::": u"\U0001f449",
735 |     "::railway_car::": u"\U0001f683",
736 |     "::triumph::": u"\U0001f624",
737 |     "::two::": u"\U00000032\U000020e3",
738 |     "::gift_heart::": u"\U0001f49d",
739 |     "::ledger::": u"\U0001f4d2",
740 |     "::sagittarius::": u"\U00002650",
741 |     "::snowflake::": u"\U00002744",
742 |     "::abc::": u"\U0001f524",
743 |     "::horse::": u"\U0001f434",
744 |     "::ok_hand::": u"\U0001f44c",
745 |     "::video_camera::": u"\U0001f4f9",
746 |     "::sparkling_heart::": u"\U0001f496",
747 |     "::taurus::": u"\U00002649",
748 |     "::frog::": u"\U0001f438",
749 |     "::hamster::": u"\U0001f439",
750 |     "::helicopter::": u"\U0001f681",
751 |     "::fries::": u"\U0001f35f",
752 |     "::mushroom::": u"\U0001f344",
753 |     "::penguin::": u"\U0001f427",
754 |     "::truck::": u"\U0001f69a",
755 |     "::bar_chart::": u"\U0001f4ca",
756 |     "::evergreen_tree::": u"\U0001f332",
757 |     "::bow::": u"\U0001f647",
758 |     "::clock12::": u"\U0001f55b",
759 |     "::four_leaf_clover::": u"\U0001f340",
760 |     "::inbox_tray::": u"\U0001f4e5",
761 |     "::smirk_cat::": u"\U0001f63c",
762 |     "::two_men_holding_hands::": u"\U0001f46c",
763 |     "::water_buffalo::": u"\U0001f403",
764 |     "::alien::": u"\U0001f47d",
765 |     "::video_game::": u"\U0001f3ae",
766 |     "::candy::": u"\U0001f36c",
767 |     "::page_facing_up::": u"\U0001f4c4",
768 |     "::watermelon::": u"\U0001f349",
769 |     "::white_check_mark::": u"\U00002705",
770 |     "::blossom::": u"\U0001f33c",
771 |     "::crocodile::": u"\U0001f40a",
772 |     "::no_mouth::": u"\U0001f636",
773 |     "::o2::": u"\U0001f17e",
774 |     "::shirt::": u"\U0001f455",
775 |     "::clock8::": u"\U0001f557",
776 |     "::eyes::": u"\U0001f440",
777 |     "::rabbit2::": u"\U0001f407",
778 |     "::tanabata_tree::": u"\U0001f38b",
779 |     "::wrench::": u"\U0001f527",
780 |     "::es::": u"\U0001f1ea\U0001f1f8",
781 |     "::trophy::": u"\U0001f3c6",
782 |     "::two_women_holding_hands::": u"\U0001f46d",
783 |     "::clock630::": u"\U0001f561",
784 |     "::pineapple::": u"\U0001f34d",
785 |     "::stuck_out_tongue::": u"\U0001f61b",
786 |     "::angry::": u"\U0001f620",
787 |     "::athletic_shoe::": u"\U0001f45f",
788 |     "::cookie::": u"\U0001f36a",
789 |     "::flags::": u"\U0001f38f",
790 |     "::game_die::": u"\U0001f3b2",
791 |     "::bird::": u"\U0001f426",
792 |     "::jack_o_lantern::": u"\U0001f383",
793 |     "::ox::": u"\U0001f402",
794 |     "::paperclip::": u"\U0001f4ce",
795 |     "::sleepy::": u"\U0001f62a",
796 |     "::astonished::": u"\U0001f632",
797 |     "::back::": u"\U0001f519",
798 |     "::closed_book::": u"\U0001f4d5",
799 |     "::hatching_chick::": u"\U0001f423",
800 |     "::arrows_clockwise::": u"\U0001f503",
801 |     "::car::": u"\U0001f697",
802 |     "::ear::": u"\U0001f442",
803 |     "::haircut::": u"\U0001f487",
804 |     "::icecream::": u"\U0001f366",
805 |     "::bust_in_silhouette::": u"\U0001f464",
806 |     "::diamonds::": u"\U00002666",
807 |     "::no_good::": u"\U0001f645",
808 |     "::pizza::": u"\U0001f355",
809 |     "::chicken::": u"\U0001f414",
810 |     "::eyeglasses::": u"\U0001f453",
811 |     "::see_no_evil::": u"\U0001f648",
812 |     "::earth_africa::": u"\U0001f30d",
813 |     "::fireworks::": u"\U0001f386",
814 |     "::page_with_curl::": u"\U0001f4c3",
815 |     "::rice_ball::": u"\U0001f359",
816 |     "::white_square_button::": u"\U0001f533",
817 |     "::cake::": u"\U0001f370",
818 |     "::red_car::": u"\U0001f697",
819 |     "::tm::": u"\U00002122",
820 |     "::unamused::": u"\U0001f612",
821 |     "::fish_cake::": u"\U0001f365",
822 |     "::key::": u"\U0001f511",
823 |     "::speedboat::": u"\U0001f6a4",
824 |     "::closed_umbrella::": u"\U0001f302",
825 |     "::pear::": u"\U0001f350",
826 |     "::satellite::": u"\U0001f4e1",
827 |     "::scream::": u"\U0001f631",
828 |     "::first_quarter_moon::": u"\U0001f313",
829 |     "::jp::": u"\U0001f1ef\U0001f1f5",
830 |     "::repeat_one::": u"\U0001f502",
831 |     "::shell::": u"\U0001f41a",
832 |     "::interrobang::": u"\U00002049",
833 |     "::trident::": u"\U0001f531",
834 |     "::u55b6::": u"\U0001f23a",
835 |     "::atm::": u"\U0001f3e7",
836 |     "::door::": u"\U0001f6aa",
837 |     "::kissing::": u"\U0001f617",
838 |     "::six_pointed_star::": u"\U0001f52f",
839 |     "::thumbsup::": u"\U0001f44d",
840 |     "::u6708::": u"\U0001f237",
841 |     "::do_not_litter::": u"\U0001f6af",
842 |     "::whale2::": u"\U0001f40b",
843 |     "::school_satchel::": u"\U0001f392",
844 |     "::cactus::": u"\U0001f335",
845 |     "::clipboard::": u"\U0001f4cb",
846 |     "::dizzy::": u"\U0001f4ab",
847 |     "::waxing_gibbous_moon::": u"\U0001f314",
848 |     "::camera::": u"\U0001f4f7",
849 |     "::capital_abcd::": u"\U0001f520",
850 |     "::leaves::": u"\U0001f343",
851 |     "::left_luggage::": u"\U0001f6c5",
852 |     "::bamboo::": u"\U0001f38d",
853 |     "::bowling::": u"\U0001f3b3",
854 |     "::eight::": u"\U00000038\U000020e3",
855 |     "::kimono::": u"\U0001f458",
856 |     "::left_right_arrow::": u"\U00002194",
857 |     "::stuck_out_tongue_winking_eye::": u"\U0001f61c",
858 |     "::surfer::": u"\U0001f3c4",
859 |     "::sweat::": u"\U0001f613",
860 |     "::violin::": u"\U0001f3bb",
861 |     "::postbox::": u"\U0001f4ee",
862 |     "::bride_with_veil::": u"\U0001f470",
863 |     "::recycle::": u"\U0000267b",
864 |     "::station::": u"\U0001f689",
865 |     "::vhs::": u"\U0001f4fc",
866 |     "::crossed_flags::": u"\U0001f38c",
867 |     "::memo::": u"\U0001f4dd",
868 |     "::no_entry::": u"\U000026d4",
869 |     "::white_circle::": u"\U000026aa",
870 |     "::arrow_lower_left::": u"\U00002199",
871 |     "::chestnut::": u"\U0001f330",
872 |     "::crystal_ball::": u"\U0001f52e",
873 |     "::last_quarter_moon::": u"\U0001f317",
874 |     "::loud_sound::": u"\U0001f50a",
875 |     "::strawberry::": u"\U0001f353",
876 |     "::worried::": u"\U0001f61f",
877 |     "::circus_tent::": u"\U0001f3aa",
878 |     "::weary::": u"\U0001f629",
879 |     "::bathtub::": u"\U0001f6c1",
880 |     "::snake::": u"\U0001f40d",
881 |     "::grin::": u"\U0001f601",
882 |     "::symbols::": u"\U0001f523",
883 |     "::airplane::": u"\U00002708",
884 |     "::heart_eyes::": u"\U0001f60d",
885 |     "::sailboat::": u"\U000026f5",
886 |     "::stew::": u"\U0001f372",
887 |     "::tshirt::": u"\U0001f455",
888 |     "::rat::": u"\U0001f400",
889 |     "::black_medium_square::": u"\U000025fc",
890 |     "::clock930::": u"\U0001f564",
891 |     "::full_moon_with_face::": u"\U0001f31d",
892 |     "::japanese_goblin::": u"\U0001f47a",
893 |     "::restroom::": u"\U0001f6bb",
894 |     "::vertical_traffic_light::": u"\U0001f6a6",
895 |     "::basketball::": u"\U0001f3c0",
896 |     "::cherry_blossom::": u"\U0001f338",
897 |     "::low_brightness::": u"\U0001f505",
898 |     "::pill::": u"\U0001f48a",
899 | }
900 | 
901 | 
902 | def emojize(text):
903 |     """
904 |     Emoji regex
905 |     """
906 |     pattern = re.compile('(::[a-z0-9\+\-_]+::)')
907 | 
908 |     def emorepl(match):
909 |         value = match.group(1)
910 |         if value in emojiCodeDict:
911 |             return emojiCodeDict[value]
912 |     return pattern.sub(emorepl, text)
913 | 


--------------------------------------------------------------------------------
/rainbowstream/image.c:
--------------------------------------------------------------------------------
 1 | /*
 2 |  * This source is borrowed from following link
 3 |  * https://github.com/jart/fabulous/blob/master/fabulous/_xterm256.c
 4 |  * I make a slightly change to fit my module here
 5 |  */
 6 | typedef struct {
 7 |         int r;
 8 |         int g;
 9 |         int b;
10 | } rgb_t;
11 | int CUBE_STEPS[] = { 0x00, 0x5F, 0x87, 0xAF, 0xD7, 0xFF };
12 | rgb_t BASIC16[] =
13 |       { {   0,   0,   0 }, { 205,   0,   0}, {   0, 205,   0 },
14 |         { 205, 205,   0 }, {   0,   0, 238}, { 205,   0, 205 },
15 |         {   0, 205, 205 }, { 229, 229, 229}, { 127, 127, 127 },
16 |         { 255,   0,   0 }, {   0, 255,   0}, { 255, 255,   0 },
17 |         {  92,  92, 255 }, { 255,   0, 255}, {   0, 255, 255 },
18 |         { 255, 255, 255 } };
19 | rgb_t COLOR_TABLE[256];
20 | 
21 | 
22 | rgb_t ansi_to_rgb(int xcolor)
23 | {
24 |   rgb_t res;
25 |   if (xcolor < 16) {
26 |     res = BASIC16[xcolor];
27 |   } else if (16 <= xcolor && xcolor <= 231) {
28 |     xcolor -= 16;
29 |     res.r = CUBE_STEPS[(xcolor / 36) % 6];
30 |     res.g = CUBE_STEPS[(xcolor / 6) % 6];
31 |     res.b = CUBE_STEPS[xcolor % 6];
32 |   } else if (232 <= xcolor && xcolor <= 255) {
33 |     res.r = res.g = res.b = 8 + (xcolor - 232) * 0x0A;
34 |   }
35 |   return res;
36 | }
37 | 
38 | int init()
39 | {
40 |   int c;
41 |   for (c = 0; c < 256; c++) {
42 |     COLOR_TABLE[c] = ansi_to_rgb(c);
43 |   }
44 |   return 0;
45 | }
46 | 
47 | int rgb_to_ansi(int r, int g, int b)
48 | {
49 |   int best_match = 0;
50 |   int smallest_distance = 1000000000;
51 |   int c, d;
52 |   for (c = 16; c < 256; c++) {
53 |     d = (COLOR_TABLE[c].r - r)*(COLOR_TABLE[c].r - r) +
54 |         (COLOR_TABLE[c].g - g)*(COLOR_TABLE[c].g - g) +
55 |         (COLOR_TABLE[c].b - b)*(COLOR_TABLE[c].b - b);
56 |     if (d < smallest_distance) {
57 |       smallest_distance = d;
58 |       best_match = c;
59 |     }
60 |   }
61 |   return best_match;
62 | }
63 | 


--------------------------------------------------------------------------------
/rainbowstream/interactive.py:
--------------------------------------------------------------------------------
 1 | import readline
 2 | import os.path
 3 | 
 4 | from .config import *
 5 | 
 6 | 
 7 | class RainbowCompleter(object):
 8 | 
 9 |     def __init__(self, options):
10 |         """
11 |         Init
12 |         """
13 |         self.options = options
14 |         self.current_candidates = []
15 |         return
16 | 
17 |     def complete(self, text, state):
18 |         """
19 |         Complete
20 |         """
21 |         response = None
22 |         if state == 0:
23 |             origline = readline.get_line_buffer()
24 |             begin = readline.get_begidx()
25 |             end = readline.get_endidx()
26 |             being_completed = origline[begin:end]
27 |             words = origline.split()
28 | 
29 |             if not words:
30 |                 self.current_candidates = sorted([c for c in self.options])
31 |             else:
32 |                 try:
33 |                     if begin == 0:
34 |                         candidates = [c for c in self.options]
35 |                     elif words[-1] in self.options[words[0]]:
36 |                         candidates = []
37 |                     else:
38 |                         first = words[0]
39 |                         candidates = self.options[first]
40 | 
41 |                     if being_completed:
42 |                         self.current_candidates = [w for w in candidates
43 |                                                    if w.startswith(being_completed)]
44 |                     else:
45 |                         self.current_candidates = candidates
46 | 
47 |                 except (KeyError, IndexError):
48 |                     self.current_candidates = []
49 | 
50 |         try:
51 |             response = self.current_candidates[state]
52 |         except IndexError:
53 |             response = None
54 |         return response
55 | 
56 | 
57 | def get_history_items():
58 |     """
59 |     Get all history item
60 |     """
61 |     return [
62 |         readline.get_history_item(i)
63 |         for i in xrange(1, readline.get_current_history_length() + 1)
64 |     ]
65 | 
66 | 
67 | def read_history():
68 |     """
69 |     Read history file
70 |     """
71 |     try:
72 |         readline.read_history_file(os.path.expanduser(c['HISTORY_FILENAME']))
73 |     except:
74 |         pass
75 | 
76 | 
77 | def save_history():
78 |     """
79 |     Save history to file
80 |     """
81 |     try:
82 |         readline.write_history_file(os.path.expanduser(c['HISTORY_FILENAME']))
83 |     except:
84 |         pass
85 | 
86 | 
87 | def init_interactive_shell(d):
88 |     """
89 |     Init the rainbow shell
90 |     """
91 |     readline.set_completer(RainbowCompleter(d).complete)
92 |     readline.parse_and_bind('set skip-completed-text on')
93 |     if 'libedit' in readline.__doc__:
94 |         readline.parse_and_bind("bind ^I rl_complete")
95 |     else:
96 |         readline.parse_and_bind("tab: complete")
97 | 


--------------------------------------------------------------------------------
/rainbowstream/pure_image.py:
--------------------------------------------------------------------------------
  1 | from PIL import Image
  2 | from functools import partial
  3 | from .config import *
  4 | from .py3patch import *
  5 | 
  6 | import sys
  7 | import os
  8 | 
  9 | """
 10 | This file is borrowed from following gist:
 11 | https://gist.github.com/MicahElliott/719710
 12 | It's too slow in compare with C program.
 13 | """
 14 | 
 15 | CLUT = [  # color look-up table
 16 |     #    8-bit, RGB hex
 17 | 
 18 |     # Primary 3-bit (8 colors). Unique representation!
 19 |     ('00', '000000'),
 20 |     ('01', '800000'),
 21 |     ('02', '008000'),
 22 |     ('03', '808000'),
 23 |     ('04', '000080'),
 24 |     ('05', '800080'),
 25 |     ('06', '008080'),
 26 |     ('07', 'c0c0c0'),
 27 | 
 28 |     # Equivalent "bright" versions of original 8 colors.
 29 |     ('08', '808080'),
 30 |     ('09', 'ff0000'),
 31 |     ('10', '00ff00'),
 32 |     ('11', 'ffff00'),
 33 |     ('12', '0000ff'),
 34 |     ('13', 'ff00ff'),
 35 |     ('14', '00ffff'),
 36 |     ('15', 'ffffff'),
 37 | 
 38 |     # Strictly ascending.
 39 |     ('16', '000000'),
 40 |     ('17', '00005f'),
 41 |     ('18', '000087'),
 42 |     ('19', '0000af'),
 43 |     ('20', '0000d7'),
 44 |     ('21', '0000ff'),
 45 |     ('22', '005f00'),
 46 |     ('23', '005f5f'),
 47 |     ('24', '005f87'),
 48 |     ('25', '005faf'),
 49 |     ('26', '005fd7'),
 50 |     ('27', '005fff'),
 51 |     ('28', '008700'),
 52 |     ('29', '00875f'),
 53 |     ('30', '008787'),
 54 |     ('31', '0087af'),
 55 |     ('32', '0087d7'),
 56 |     ('33', '0087ff'),
 57 |     ('34', '00af00'),
 58 |     ('35', '00af5f'),
 59 |     ('36', '00af87'),
 60 |     ('37', '00afaf'),
 61 |     ('38', '00afd7'),
 62 |     ('39', '00afff'),
 63 |     ('40', '00d700'),
 64 |     ('41', '00d75f'),
 65 |     ('42', '00d787'),
 66 |     ('43', '00d7af'),
 67 |     ('44', '00d7d7'),
 68 |     ('45', '00d7ff'),
 69 |     ('46', '00ff00'),
 70 |     ('47', '00ff5f'),
 71 |     ('48', '00ff87'),
 72 |     ('49', '00ffaf'),
 73 |     ('50', '00ffd7'),
 74 |     ('51', '00ffff'),
 75 |     ('52', '5f0000'),
 76 |     ('53', '5f005f'),
 77 |     ('54', '5f0087'),
 78 |     ('55', '5f00af'),
 79 |     ('56', '5f00d7'),
 80 |     ('57', '5f00ff'),
 81 |     ('58', '5f5f00'),
 82 |     ('59', '5f5f5f'),
 83 |     ('60', '5f5f87'),
 84 |     ('61', '5f5faf'),
 85 |     ('62', '5f5fd7'),
 86 |     ('63', '5f5fff'),
 87 |     ('64', '5f8700'),
 88 |     ('65', '5f875f'),
 89 |     ('66', '5f8787'),
 90 |     ('67', '5f87af'),
 91 |     ('68', '5f87d7'),
 92 |     ('69', '5f87ff'),
 93 |     ('70', '5faf00'),
 94 |     ('71', '5faf5f'),
 95 |     ('72', '5faf87'),
 96 |     ('73', '5fafaf'),
 97 |     ('74', '5fafd7'),
 98 |     ('75', '5fafff'),
 99 |     ('76', '5fd700'),
100 |     ('77', '5fd75f'),
101 |     ('78', '5fd787'),
102 |     ('79', '5fd7af'),
103 |     ('80', '5fd7d7'),
104 |     ('81', '5fd7ff'),
105 |     ('82', '5fff00'),
106 |     ('83', '5fff5f'),
107 |     ('84', '5fff87'),
108 |     ('85', '5fffaf'),
109 |     ('86', '5fffd7'),
110 |     ('87', '5fffff'),
111 |     ('88', '870000'),
112 |     ('89', '87005f'),
113 |     ('90', '870087'),
114 |     ('91', '8700af'),
115 |     ('92', '8700d7'),
116 |     ('93', '8700ff'),
117 |     ('94', '875f00'),
118 |     ('95', '875f5f'),
119 |     ('96', '875f87'),
120 |     ('97', '875faf'),
121 |     ('98', '875fd7'),
122 |     ('99', '875fff'),
123 |     ('100', '878700'),
124 |     ('101', '87875f'),
125 |     ('102', '878787'),
126 |     ('103', '8787af'),
127 |     ('104', '8787d7'),
128 |     ('105', '8787ff'),
129 |     ('106', '87af00'),
130 |     ('107', '87af5f'),
131 |     ('108', '87af87'),
132 |     ('109', '87afaf'),
133 |     ('110', '87afd7'),
134 |     ('111', '87afff'),
135 |     ('112', '87d700'),
136 |     ('113', '87d75f'),
137 |     ('114', '87d787'),
138 |     ('115', '87d7af'),
139 |     ('116', '87d7d7'),
140 |     ('117', '87d7ff'),
141 |     ('118', '87ff00'),
142 |     ('119', '87ff5f'),
143 |     ('120', '87ff87'),
144 |     ('121', '87ffaf'),
145 |     ('122', '87ffd7'),
146 |     ('123', '87ffff'),
147 |     ('124', 'af0000'),
148 |     ('125', 'af005f'),
149 |     ('126', 'af0087'),
150 |     ('127', 'af00af'),
151 |     ('128', 'af00d7'),
152 |     ('129', 'af00ff'),
153 |     ('130', 'af5f00'),
154 |     ('131', 'af5f5f'),
155 |     ('132', 'af5f87'),
156 |     ('133', 'af5faf'),
157 |     ('134', 'af5fd7'),
158 |     ('135', 'af5fff'),
159 |     ('136', 'af8700'),
160 |     ('137', 'af875f'),
161 |     ('138', 'af8787'),
162 |     ('139', 'af87af'),
163 |     ('140', 'af87d7'),
164 |     ('141', 'af87ff'),
165 |     ('142', 'afaf00'),
166 |     ('143', 'afaf5f'),
167 |     ('144', 'afaf87'),
168 |     ('145', 'afafaf'),
169 |     ('146', 'afafd7'),
170 |     ('147', 'afafff'),
171 |     ('148', 'afd700'),
172 |     ('149', 'afd75f'),
173 |     ('150', 'afd787'),
174 |     ('151', 'afd7af'),
175 |     ('152', 'afd7d7'),
176 |     ('153', 'afd7ff'),
177 |     ('154', 'afff00'),
178 |     ('155', 'afff5f'),
179 |     ('156', 'afff87'),
180 |     ('157', 'afffaf'),
181 |     ('158', 'afffd7'),
182 |     ('159', 'afffff'),
183 |     ('160', 'd70000'),
184 |     ('161', 'd7005f'),
185 |     ('162', 'd70087'),
186 |     ('163', 'd700af'),
187 |     ('164', 'd700d7'),
188 |     ('165', 'd700ff'),
189 |     ('166', 'd75f00'),
190 |     ('167', 'd75f5f'),
191 |     ('168', 'd75f87'),
192 |     ('169', 'd75faf'),
193 |     ('170', 'd75fd7'),
194 |     ('171', 'd75fff'),
195 |     ('172', 'd78700'),
196 |     ('173', 'd7875f'),
197 |     ('174', 'd78787'),
198 |     ('175', 'd787af'),
199 |     ('176', 'd787d7'),
200 |     ('177', 'd787ff'),
201 |     ('178', 'd7af00'),
202 |     ('179', 'd7af5f'),
203 |     ('180', 'd7af87'),
204 |     ('181', 'd7afaf'),
205 |     ('182', 'd7afd7'),
206 |     ('183', 'd7afff'),
207 |     ('184', 'd7d700'),
208 |     ('185', 'd7d75f'),
209 |     ('186', 'd7d787'),
210 |     ('187', 'd7d7af'),
211 |     ('188', 'd7d7d7'),
212 |     ('189', 'd7d7ff'),
213 |     ('190', 'd7ff00'),
214 |     ('191', 'd7ff5f'),
215 |     ('192', 'd7ff87'),
216 |     ('193', 'd7ffaf'),
217 |     ('194', 'd7ffd7'),
218 |     ('195', 'd7ffff'),
219 |     ('196', 'ff0000'),
220 |     ('197', 'ff005f'),
221 |     ('198', 'ff0087'),
222 |     ('199', 'ff00af'),
223 |     ('200', 'ff00d7'),
224 |     ('201', 'ff00ff'),
225 |     ('202', 'ff5f00'),
226 |     ('203', 'ff5f5f'),
227 |     ('204', 'ff5f87'),
228 |     ('205', 'ff5faf'),
229 |     ('206', 'ff5fd7'),
230 |     ('207', 'ff5fff'),
231 |     ('208', 'ff8700'),
232 |     ('209', 'ff875f'),
233 |     ('210', 'ff8787'),
234 |     ('211', 'ff87af'),
235 |     ('212', 'ff87d7'),
236 |     ('213', 'ff87ff'),
237 |     ('214', 'ffaf00'),
238 |     ('215', 'ffaf5f'),
239 |     ('216', 'ffaf87'),
240 |     ('217', 'ffafaf'),
241 |     ('218', 'ffafd7'),
242 |     ('219', 'ffafff'),
243 |     ('220', 'ffd700'),
244 |     ('221', 'ffd75f'),
245 |     ('222', 'ffd787'),
246 |     ('223', 'ffd7af'),
247 |     ('224', 'ffd7d7'),
248 |     ('225', 'ffd7ff'),
249 |     ('226', 'ffff00'),
250 |     ('227', 'ffff5f'),
251 |     ('228', 'ffff87'),
252 |     ('229', 'ffffaf'),
253 |     ('230', 'ffffd7'),
254 |     ('231', 'ffffff'),
255 | 
256 |     # Gray-scale range.
257 |     ('232', '080808'),
258 |     ('233', '121212'),
259 |     ('234', '1c1c1c'),
260 |     ('235', '262626'),
261 |     ('236', '303030'),
262 |     ('237', '3a3a3a'),
263 |     ('238', '444444'),
264 |     ('239', '4e4e4e'),
265 |     ('240', '585858'),
266 |     ('241', '626262'),
267 |     ('242', '6c6c6c'),
268 |     ('243', '767676'),
269 |     ('244', '808080'),
270 |     ('245', '8a8a8a'),
271 |     ('246', '949494'),
272 |     ('247', '9e9e9e'),
273 |     ('248', 'a8a8a8'),
274 |     ('249', 'b2b2b2'),
275 |     ('250', 'bcbcbc'),
276 |     ('251', 'c6c6c6'),
277 |     ('252', 'd0d0d0'),
278 |     ('253', 'dadada'),
279 |     ('254', 'e4e4e4'),
280 |     ('255', 'eeeeee'),
281 | ]
282 | 
283 | 
284 | def _create_dicts():
285 |     """
286 |     Create dictionary
287 |     """
288 |     short2rgb_dict = dict(CLUT)
289 |     rgb2short_dict = {}
290 |     for k, v in short2rgb_dict.items():
291 |         rgb2short_dict[v] = k
292 |     return rgb2short_dict, short2rgb_dict
293 | 
294 | RGB2SHORT_DICT, SHORT2RGB_DICT = _create_dicts()
295 | 
296 | 
297 | def short2rgb(short):
298 |     """
299 |     Short to RGB
300 |     """
301 |     return SHORT2RGB_DICT[short]
302 | 
303 | 
304 | def pixel_print(ansicolor):
305 |     """
306 |     Print a pixel with given Ansi color
307 |     """
308 |     sys.stdout.write('\033[48;5;%sm \033[0m' % (ansicolor))
309 | 
310 | 
311 | def hex_to_rgb(value):
312 |     """
313 |     Hex to RGB
314 |     """
315 |     value = value.lstrip('#')
316 |     lv = len(value)
317 |     return tuple(int(value[i:i + lv / 3], 16) for i in xrange(0, lv, lv / 3))
318 | 
319 | 
320 | def rgb_to_hex(rgb):
321 |     """
322 |     RGB to Hex
323 |     """
324 |     return '%02x%02x%02x' % rgb
325 | 
326 | 
327 | def rgb2short(r, g, b):
328 |     """
329 |     RGB to short
330 |     """
331 |     dist = lambda s, d: (s[0] - d[0]) ** 2 + \
332 |         (s[1] - d[1]) ** 2 + (s[2] - d[2]) ** 2
333 |     ary = [hex_to_rgb(hex) for hex in RGB2SHORT_DICT]
334 |     m = min(ary, key=partial(dist, (r, g, b)))
335 |     return RGB2SHORT_DICT[rgb_to_hex(m)]
336 | 
337 | 
338 | def image_to_display(path, start=None, length=None):
339 |     """
340 |     Display an image
341 |     """
342 |     rows, columns = os.popen('stty size', 'r').read().split()
343 |     if not start:
344 |         start = IMAGE_SHIFT
345 |     if not length:
346 |         length = int(columns) - 2 * start
347 |     i = Image.open(path)
348 |     i = i.convert('RGBA')
349 |     w, h = i.size
350 |     i.load()
351 |     width = min(w, length)
352 |     height = int(float(h) * (float(width) / float(w)))
353 |     height //= 2
354 |     i = i.resize((width, height), Image.BICUBIC)
355 |     height = min(height, IMAGE_MAX_HEIGHT)
356 | 
357 |     for y in xrange(height):
358 |         sys.stdout.write(' ' * start)
359 |         for x in xrange(width):
360 |             p = i.getpixel((x, y))
361 |             r, g, b = p[:3]
362 |             pixel_print(rgb2short(r, g, b))
363 |         sys.stdout.write('\n')
364 | 
365 | 
366 | """
367 | For direct using purpose
368 | """
369 | if __name__ == '__main__':
370 |     image_to_display(sys.argv[1])
371 | 


--------------------------------------------------------------------------------
/rainbowstream/py3patch.py:
--------------------------------------------------------------------------------
 1 | import sys
 2 | 
 3 | # Library compatibility
 4 | if sys.version[0] == "2":
 5 |     from HTMLParser import HTMLParser
 6 |     from urllib2 import URLError
 7 |     unescape = HTMLParser().unescape
 8 | else:
 9 |     from html.parser import HTMLParser
10 |     from urllib.error import URLError
11 |     # HTMLParser().unescape is deprecated since Python 3.4 and is unsafe.
12 |     # you should use html.unescape instead, like you've said
13 |     from html import unescape
14 | 
15 | # unescape = HTMLParser().unescape
16 | # According to https://github.com/python/cpython/blob/master/Lib/html/parser.py#L547 ,
17 | # in python 3.5 maybe I should use
18 | # from html import unescape
19 | # ~~but it is a far-future story:)~~ no longer. 
20 | 
21 | 
22 | 
23 | # Function compatibility
24 | # xrange, raw_input, map ,unicde
25 | if sys.version[0] == "2":
26 |     lmap = lambda f, a: map(f, a)
27 |     str2u = lambda x: x.decode('utf-8')
28 |     u2str = lambda x: x.encode('utf-8')
29 | else:
30 |     xrange = range
31 |     raw_input = input
32 |     lmap = lambda f, a: list(map(f, a))
33 |     str2u = u2str = lambda x: x
34 | 


--------------------------------------------------------------------------------
/rainbowstream/util.py:
--------------------------------------------------------------------------------
 1 | import json
 2 | 
 3 | from twitter.util import printNicely
 4 | from .colors import *
 5 | from .config import *
 6 | 
 7 | 
 8 | def detail_twitter_error(twitterException):
 9 |     """
10 |     Display Twitter Errors nicely
11 |     """
12 |     data = twitterException.response_data
13 |     try:
14 |         for m in data.get('errors', dict()):
15 |             printNicely(yellow(m.get('message')))
16 |     except:
17 |         printNicely(yellow(data))
18 | 
19 | 
20 | def format_prefix(listname='', keyword=''):
21 |     """
22 |     Format the custom prefix
23 |     """
24 |     formattedPrefix = c['PREFIX']
25 |     owner = '@' + c['original_name']
26 |     place = ''
27 |     # Public stream
28 |     if keyword:
29 |         formattedPrefix = ''.join(formattedPrefix.split('#owner'))
30 |         formattedPrefix = ''.join(formattedPrefix.split('#place'))
31 |         formattedPrefix = ''.join(formattedPrefix.split('#me'))
32 |     # List stream
33 |     elif listname:
34 |         formattedPrefix = ''.join(formattedPrefix.split('#keyword'))
35 |         formattedPrefix = ''.join(formattedPrefix.split('#me'))
36 |         owner, place = listname.split('/')
37 |         place = '/' + place
38 |     # Personal stream
39 |     else:
40 |         formattedPrefix = ''.join(formattedPrefix.split('#keyword'))
41 |         formattedPrefix = ''.join(formattedPrefix.split('#owner'))
42 |         formattedPrefix = ''.join(formattedPrefix.split('#place'))
43 | 
44 |     formattedPrefix = formattedPrefix.replace('#owner', owner)
45 |     formattedPrefix = formattedPrefix.replace('#place', place)
46 |     formattedPrefix = formattedPrefix.replace('#keyword', keyword)
47 |     formattedPrefix = formattedPrefix.replace('#me', '@' + c['original_name'])
48 | 
49 |     return formattedPrefix
50 | 
51 | 
52 | def add_tweetmode_parameter(kwargs):
53 |     """
54 |     Add support for extended mode to Twitter API calls unless explicitly stated in config
55 |     """
56 |     if not c.get('DISABLE_EXTENDED_TWEETS'):
57 |         kwargs['tweet_mode'] = 'extended'
58 |     return kwargs
59 | 


--------------------------------------------------------------------------------
/release.sh:
--------------------------------------------------------------------------------
1 | # Packaging
2 | python setup.py sdist bdist_wheel
3 | # Check distribution package
4 | twine check dist/*
5 | # Upload to PyPi
6 | twine upload dist/*
7 | # Announce the latest version to Github
8 | git push origin master
9 | 


--------------------------------------------------------------------------------
/screenshot/RainbowStreamAll.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/orakaro/rainbowstream/8b2efa52574865646adfb157f4563a72d9e37d96/screenshot/RainbowStreamAll.png


--------------------------------------------------------------------------------
/screenshot/rs.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/orakaro/rainbowstream/8b2efa52574865646adfb157f4563a72d9e37d96/screenshot/rs.gif


--------------------------------------------------------------------------------
/screenshot/themes/Default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/orakaro/rainbowstream/8b2efa52574865646adfb157f4563a72d9e37d96/screenshot/themes/Default.png


--------------------------------------------------------------------------------
/screenshot/themes/Monokai.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/orakaro/rainbowstream/8b2efa52574865646adfb157f4563a72d9e37d96/screenshot/themes/Monokai.png


--------------------------------------------------------------------------------
/screenshot/themes/Solarized.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/orakaro/rainbowstream/8b2efa52574865646adfb157f4563a72d9e37d96/screenshot/themes/Solarized.png


--------------------------------------------------------------------------------
/screenshot/themes/TomorrowNight.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/orakaro/rainbowstream/8b2efa52574865646adfb157f4563a72d9e37d96/screenshot/themes/TomorrowNight.png


--------------------------------------------------------------------------------
/screenshot/themes/larapaste.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/orakaro/rainbowstream/8b2efa52574865646adfb157f4563a72d9e37d96/screenshot/themes/larapaste.png


--------------------------------------------------------------------------------
/setup.CFG:
--------------------------------------------------------------------------------
1 | [metadata]
2 | description-file = README.rst
3 | 


--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
 1 | from setuptools import setup, find_packages
 2 | import os
 3 | import os.path
 4 | import sys
 5 | 
 6 | if sys.version[0] == "2":
 7 |     from pipes import quote
 8 | else:
 9 |     from shlex import quote
10 | 
11 | # Bumped version
12 | version = '1.6.0'
13 | 
14 | # Require
15 | install_requires = [
16 |     "python-dateutil",
17 |     "arrow",
18 |     "requests",
19 |     "pyfiglet",
20 |     "twitter",
21 |     "Pillow",
22 |     "PySocks",
23 |     "pocket",
24 |     "libsixel-python",
25 |     "resize-image"
26 | ]
27 | 
28 | # Default user (considers non virtualenv method)
29 | user = os.environ.get('SUDO_USER', os.environ.get('USER', None))
30 | 
31 | # Copy default config if not exists
32 | default = os.path.expanduser("~") + os.sep + '.rainbow_config.json'
33 | if not os.path.isfile(default):
34 |     cmd = 'cp rainbowstream/colorset/config ' + default
35 |     os.system(cmd)
36 |     if user:
37 |         cmd = 'chown ' + quote(user) + ' ' + default
38 |         os.system(cmd)
39 |     cmd = 'chmod 777 ' + default
40 |     os.system(cmd)
41 | 
42 | # Setup
43 | setup(name='rainbowstream',
44 |       version=version,
45 |       description="A smart and nice Twitter client on terminal.",
46 |       long_description=open("./README.rst", "r").read(),
47 |       classifiers=[
48 |           "Development Status :: 5 - Production/Stable",
49 |           "Environment :: Console",
50 |           "Intended Audience :: End Users/Desktop",
51 |           "Natural Language :: English",
52 |           "Operating System :: OS Independent",
53 |           "Programming Language :: Python :: 2.7",
54 |           "Programming Language :: Python :: 3.2",
55 |           "Programming Language :: Python :: 3.3",
56 |           "Programming Language :: Python :: 3.4",
57 |           "Programming Language :: Python :: 3.5",
58 |           "Programming Language :: Python :: 3.6",
59 |           "Programming Language :: Python :: 3.7",
60 |           "Topic :: Internet :: WWW/HTTP :: Dynamic Content :: CGI Tools/Libraries",
61 |           "Topic :: Utilities",
62 |           "License :: OSI Approved :: MIT License",
63 |       ],
64 |       keywords='twitter, command-line tools, stream API',
65 |       author='Vu Nhat Minh',
66 |       author_email='nhatminh179@gmail.com',
67 |       url='http://www.rainbowstream.org/',
68 |       license='MIT License',
69 |       packages=find_packages(exclude=['ez_setup', 'examples', 'tests']),
70 |       include_package_data=True,
71 |       zip_safe=True,
72 |       install_requires=install_requires,
73 |       long_description_content_type='text/markdown',
74 |       entry_points="""
75 |       # -*- Entry points: -*-
76 |       [console_scripts]
77 |       rainbowstream=rainbowstream.rainbow:fly
78 |       """,
79 |       )
80 | 


--------------------------------------------------------------------------------
/theme.md:
--------------------------------------------------------------------------------
  1 | ## Available themes
  2 | #### Monokai (`monokai`)
  3 | ![Monokai](./screenshot/themes/Monokai.png)
  4 | #### Solarized (`solarized`)
  5 | ![Solarized](./screenshot/themes/Solarized.png)
  6 | #### Tomorrow Night (`tomorrow_night`)
  7 | ![Solarized](./screenshot/themes/TomorrowNight.png)
  8 | #### Larapaste (`larapaste`)
  9 | ![Solarized](./screenshot/themes/larapaste.png)
 10 | 
 11 | ## Customization
 12 | Modify `~/.rainbow_config.json` and follow next instruction.
 13 | 
 14 | Examples are available in
 15 | [Themes folder](https://github.com/DTVD/rainbowstream/blob/master/rainbowstream/colorset)
 16 | 
 17 | ### Custom config
 18 |  * There is a file named exactly `.rainbow_config.json` and is placed at your home directory.
 19 |  * Add color configurations to above file and follow json format.
 20 |  * Comments as `//` or `/*...*/` are allowed.
 21 |  * Here is an example
 22 | 
 23 | ```json
 24 | {
 25 |     "DECORATED_NAME" : 198,
 26 |     "CYCLE_COLOR" :[198,57,166,50,179,74,112],
 27 |     "TWEET" : {
 28 |         "mynick"          : 179,
 29 |         "nick"            : 112,
 30 |         "clock"           : 57,
 31 |         "id"              : 166,
 32 |         "client"          : 74,
 33 |         "favorited"       : 50,
 34 |         "retweet_count"   : 50,
 35 |         "favorite_count"  : 198,
 36 |         "rt"              : 179,
 37 |         "link"            : 74,
 38 |         "hashtag"         : 198,
 39 |         "mytweet"         : 179,
 40 |         "keyword"         : "on_light_green"
 41 |     },
 42 | 
 43 |     "NOTIFICATION":{
 44 |         "source_nick"   : 112,
 45 |         "notify"        : 179,
 46 |         "clock"         : 57
 47 |     },
 48 | 
 49 |     "MESSAGE" : {
 50 |         "partner"       : 112,
 51 |         "me"            : 112,
 52 |         "partner_frame" : 198,
 53 |         "me_frame"      : 74,
 54 |         "sender"        : 112,
 55 |         "recipient"     : 112,
 56 |         "to"            : 50,
 57 |         "clock"         : 57,
 58 |         "id"            : 166
 59 |     },
 60 | 
 61 |     "PROFILE" : {
 62 |         "statuses_count"    : 112,
 63 |         "friends_count"     : 198,
 64 |         "followers_count"   : 57,
 65 |         "nick"              : 198,
 66 |         "profile_image_url" : 74,
 67 |         "description"       : 166,
 68 |         "location"          : 112,
 69 |         "url"               : 74,
 70 |         "clock"             : 57
 71 |     },
 72 | 
 73 |     "TREND" : {
 74 |         "url": 74
 75 |     },
 76 | 
 77 |     "CAL" : {
 78 |         "days": 57,
 79 |         "today": "on_light_blue"
 80 |     },
 81 | 
 82 |     "GROUP" : {
 83 |         "name": 112,
 84 |         "member": 57,
 85 |         "subscriber": 198,
 86 |         "mode": 112,
 87 |         "description": 166,
 88 |         "clock": 57
 89 |     }
 90 | }
 91 | ```
 92 | 
 93 | ### Available Colors
 94 | 
 95 | There are 16 basic colors:
 96 |   * default
 97 |   * black
 98 |   * red
 99 |   * green
100 |   * yellow
101 |   * blue
102 |   * magenta
103 |   * cyan
104 |   * grey
105 |   * light_red
106 |   * light_green
107 |   * light_yellow
108 |   * light_blue
109 |   * light_magenta
110 |   * light_cyan
111 |   * white
112 | 
113 | These colors will be enough for almost terminals.
114 | But if your terminal can support 256 colors (check your `$TERM` variable!),
115 | you can even use 0 to 255 as the example above.
116 | 
117 | There are also background highlight colors like:
118 |   * on_default
119 |   * on_black
120 |   * on_red
121 |   * on_green
122 |   * on_yellow
123 |   * on_blue
124 |   * on_magenta
125 |   * on_cyan
126 |   * on_grey
127 |   * on_light_red
128 |   * on_light_green
129 |   * on_light_yellow
130 |   * on_light_blue
131 |   * on_light_magenta
132 |   * on_light_cyan
133 |   * on_white
134 | 
135 | 
136 | Color reference can be found at
137 | [bash colors](http://misc.flogisoft.com/bash/tip_colors_and_formatting) or
138 | [256 xterm colors](http://www.calmar.ws/vim/256-xterm-24bit-rgb-color-chart.html).
139 | 
140 | ### Available options
141 | * `DECORATED_NAME`: color of your Twitter's __username__ which is placed at every line's begin.
142 | * `CYCLE_COLOR`: list of colors from which Twitter __real name__ 's color is selected.
143 |   * Color selection is cycle through this list but with _memoization_.
144 |   * It's means that same names will appear in same colors.
145 | * `TWEET`: colors of parts in a tweet's ouput.
146 |   * `mynick` : color for your Twitter __username__.
147 |   * `nick` : color for other Twitter __username__.
148 |   * `clock`: color for time of tweet.
149 |   * `id`: color for tweet's id.
150 |   * `client`: color for used Twitter client.
151 |   * `favorite`: color for the star symbol when a tweet is favorited by you.
152 |   * `retweet_count`: color for retweets count.
153 |   * `favorite_count`: color for favorites count.
154 |   * `rt`: color for `RT` word in tweet's content.
155 |   * `link`: color for an url.
156 |   * `hashtag`: color for a hashtag.
157 |   * `mytweet`: color for tweet's text from yourself.
158 |   * `keyword`: color for highlighted keyword (in tweets search).
159 | * `NOTIFICATION`: colors of notification events.
160 |   * `source_nick`: color for user's __username__.
161 |   * `notify`: color for notification message.
162 |   * `clock`: color for time of notification event.
163 | * `MESSAGE`: colors of parts in message's output.
164 |   * `partner`: color for __partner__.
165 |   * `me`: color for __authenticated user__.
166 |   * `partner_frame`: color for __partner's frame__.
167 |   * `me_frame`: color for __authenticated user's frame__.
168 |   * `sender`: color for sender's __username__.
169 |   * `recipient`: color for recipient's __username__.
170 |   * `to`: color for the `>>>` symbol.
171 |   * `clock`: color for time of message.
172 |   * `id`: color for message's id.
173 | * `PROFILE`: colors for parts in profile's ouput.
174 |   * `statuses_count`: color for statuses count.
175 |   * `friends_count`: color for friends count.
176 |   * `followers_count`: color for followers count.
177 |   * `nick`: color for Twitter __username__.
178 |   * `profile_image_url`: color for profile image url.
179 |   * `description`: color for description.
180 |   * `location`: color for location.
181 |   * `url`: color for url.
182 |   * `clock`: color for joined time.
183 | * `TREND`: colors for trend's output:
184 |   * `url`: color for trend's url.
185 | * `CAL`: colors for calendar's output:
186 |   * `days`: color for days in current month.
187 |   * `today`: color for today.
188 | * `GROUP`: colors for twitter list output:
189 |   * `name`: color for twitter list's name.
190 |   * `member`: color member count.
191 |   * `subscriber`: color subscriber count.
192 |   * `mode`: color twitter list's mode.
193 |   * `description`: color twitter list's description.
194 |   * `clock`: color twitter list's created time.
195 | 
196 | ### Theme usage
197 | While entered Rainbow Stream:
198 | * `theme` and hit ENTER to see which is available.
199 | * `theme` + TAB twice will show themes list instantly.
200 | * `theme monokai` will apply `monokai` theme immediately. You can use TAB key for theme's name autocompletion.
201 | 
202 | ### Theme contribution
203 | I appreciate any contribution for themes for this app.
204 | Please add a file to [themes folder](https://github.com/DTVD/rainbowstream/tree/master/rainbowstream/colorset)
205 | (json format!) and create a [pull request](https://github.com/DTVD/rainbowstream/compare/) with a screenshot.
206 | 


--------------------------------------------------------------------------------