-
Notifications
You must be signed in to change notification settings - Fork 62
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Jim Easterbrook <jim@jim-easterbrook.me.uk>
- Loading branch information
1 parent
5bfc528
commit 7f1e07e
Showing
6 changed files
with
280 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
__version__ = '18.8.0' | ||
_release = '1563' | ||
_commit = '46de5de' | ||
_release = '1564' | ||
_commit = '5bfc528' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
media /tmp/pywws/results/tweet.png | ||
#live# | ||
#timezone local# | ||
#roundtime True# | ||
#idx "%H:%M %Z: "# | ||
temperature #temp_out "%.1f�C" "-"# | ||
, humidity #hum_out "%d%%" "-"# | ||
#hourly# | ||
, wind #wind_dir "%s" "-" "winddir_text(x)"# #wind_ave "%.1f" "-" "wind_mph(x)"# | ||
mph ave, #wind_gust "%.1f" "-" "wind_mph(x)"# | ||
mph gust, rain #rain "%.1f mm/hr"# | ||
#live# | ||
, pressure #rel_pressure "%.1f hPa "# | ||
#hourly# | ||
#pressure_trend "%s" "" "pressure_trend_text(x)"# | ||
|
||
##pywws ##weather ##epsom ##ewell |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
#live# | ||
#timezone local# | ||
#idx "%H:%M %Z: "# | ||
#hourly# | ||
Unreliable forecast for the next 12 hours: #calc "Zambretti(params, data)"# | ||
|
||
##pywws ##weather ##epsom ##ewell |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,252 @@ | ||
# pywws - Python software for USB Wireless Weather Stations | ||
# http://github.com/jim-easterbrook/pywws | ||
# Copyright (C) 2018 pywws contributors | ||
|
||
# This program is free software; you can redistribute it and/or | ||
# modify it under the terms of the GNU General Public License | ||
# as published by the Free Software Foundation; either version 2 | ||
# of the License, or (at your option) any later version. | ||
|
||
# This program is distributed in the hope that it will be useful, | ||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
# GNU General Public License for more details. | ||
|
||
# You should have received a copy of the GNU General Public License | ||
# along with this program; if not, write to the Free Software | ||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
|
||
"""Post messages to Mastodon. | ||
Mastodon_ is a micro-blogging system that at first sight looks a bit | ||
like Twitter. In many ways it's quite different though. This module | ||
sends "toots", with optional image files, typically to report on weather | ||
conditions every hour. | ||
* Create account: https://joinmastodon.org/ | ||
* Additional dependency: https://mastodonpy.readthedocs.io/ | ||
* Example ``weather.ini`` configuration:: | ||
[mastodon] | ||
handle = kt19weather@botsin.space | ||
client_id = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | ||
client_secret = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | ||
access_token = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | ||
[hourly] | ||
text = [('toot.txt', 'L')] | ||
plot = [('tweet.png.xml', 'L')] | ||
services = [('mastodon', '/tmp/pywws/results/toot.txt')] | ||
Create an account | ||
----------------- | ||
Before creating a Mastodon account for your weather reports you need to | ||
choose an "instance". Mastodon is a federated system, running on many | ||
interconnected servers, each of which is called an instance. Choose one | ||
whose rules allow "bots" (i.e. automated posts) such as `botsin.space`_. | ||
After creating an account, edit its profile and make sure the "this is a | ||
bot account" box is selected. | ||
The :py:mod:`pywws.service.mastodon` module requires you to install an | ||
additional dependency:: | ||
sudo pip install mastodon.py | ||
Authorise pywws | ||
--------------- | ||
Before you can send "toots" you need to authorise pywws to post to your | ||
account. If you run pywws on a low power device such as a Raspberry Pi, | ||
you may find it easier to run this authorisation step on another | ||
computer, as long as it has the required dependencies installed. You can | ||
use an empty 'data' directory -- a ``weather.ini`` file will be created | ||
whose contents can be copied into your real ``weather.ini`` file using | ||
any text editor. | ||
Make sure no other pywws software is running, then run the module with | ||
the ``-r`` option. (Remember to replace ``data_dir`` with your data | ||
directory.) :: | ||
python -m pywws.service.mastodon -r data_dir | ||
The first time you do this it will probably crash because you haven't | ||
set your Mastodon "handle" in ``weather.ini``. Edit ``weather.ini`` and | ||
add your handle as shown above, then run the module with the ``-r`` | ||
option again. | ||
This will open a web browser window (or give you a URL to copy to your | ||
web browser) where you can log in to your Mastodon account and authorise | ||
pywws to post. If the login is successful the browser will display a | ||
long code string which you then copy to pywws:: | ||
jim@brains:~$ python3 -m pywws.service.mastodon -r weather_data | ||
07:45:34:pywws.logger:pywws version 18.8.0, build 1564 (5bfc528) | ||
Please enter the auth code shown in your web browser: 12573ba24341b5de2a1f2930fc93889c3576af7b50ecd9d713fa502d773805b4 | ||
jim@brains:~$ | ||
The ``access_token`` value stored in ``weather.ini`` gives access to | ||
your Mastodon account and should be kept confidential. | ||
Create a template | ||
----------------- | ||
A "toot" is a short text of up to 500 characters. It's up to you what to | ||
put in your "toots" but an example is included to get your started. Copy | ||
the example template ``toot.txt`` to your template directory, then edit | ||
it to suit your preferences. (At least change the hashtags to suit your | ||
location.) You should also check it uses the same text encoding as your | ||
other templates. The example template includes a ``media`` line to send | ||
a graph. Either remove this or copy the example graph template | ||
``tweet.png.xml`` to your graph templates directory, if you don't | ||
already have one there, and edit the ``media`` line to use your | ||
``local_files`` directory. | ||
Now generate a toot file from your template, for example:: | ||
python -m pywws.template ~/weather/data ~/weather/templates/toot.txt toot.txt | ||
cat toot.txt | ||
Post your first toot | ||
-------------------- | ||
Now you are ready to run :py:mod:`pywws.service.mastodon`:: | ||
python -m pywws.service.mastodon ~/weather/data toot.txt | ||
If this works, your new Mastodon account will have posted its first | ||
weather report. (You can delete the toot.txt file now.) | ||
Add Mastodon posts to your hourly tasks | ||
--------------------------------------- | ||
Edit the ``[hourly]`` section in ``weather.ini``. If your toots include | ||
one or more graphs you need to add the graph templates to the ``plot`` | ||
list, with an ``L`` flag to store the result in your ``local_files`` | ||
directory. Note that if you reuse your Twitter graph you only need to | ||
generate it once. Add your toot template to the ``text`` list, again | ||
with an ``L`` flag to store the result locally. Finally, add | ||
``mastodon`` to the ``services`` list, with an option specifying the | ||
full path of the template processing result. For example:: | ||
[hourly] | ||
text = [('toot.txt', 'L')] | ||
plot = [('tweet.png.xml', 'L')] | ||
services = [('mastodon', '/tmp/pywws/results/toot.txt')] | ||
You could use the ``[logged]``, ``[12 hourly]`` or ``[daily]`` sections | ||
instead, but I think ``[hourly]`` is most appropriate for Mastodon | ||
updates. | ||
Add more images | ||
--------------- | ||
Mastodon allows up to four images per "toot", so you could add more | ||
graphs, or a webcam image, or a weather forecast icon. Use one ``media`` | ||
line per image at the start of your toot template and specify the full | ||
path for each one. | ||
.. _botsin.space: https://botsin.space/ | ||
.. _Mastodon: https://joinmastodon.org/ | ||
""" | ||
|
||
from __future__ import absolute_import, print_function | ||
|
||
import codecs | ||
from contextlib import contextmanager | ||
from datetime import timedelta | ||
import logging | ||
import os | ||
import sys | ||
|
||
from mastodon import Mastodon | ||
|
||
import pywws.localisation | ||
import pywws.logger | ||
import pywws.service | ||
import pywws.storage | ||
|
||
__docformat__ = "restructuredtext en" | ||
service_name = os.path.splitext(os.path.basename(__file__))[0] | ||
logger = logging.getLogger(__name__) | ||
|
||
|
||
class ToService(pywws.service.FileService): | ||
logger = logger | ||
service_name = service_name | ||
|
||
def __init__(self, context): | ||
# check config | ||
handle = context.params.get(service_name, 'handle', '') | ||
if not handle: | ||
raise RuntimeError('No user handle specified in weather.ini') | ||
# get default character encoding of template output | ||
self.encoding = context.params.get( | ||
'config', 'template encoding', 'iso-8859-1') | ||
# base class init | ||
super(ToService, self).__init__(context) | ||
|
||
@contextmanager | ||
def session(self): | ||
handle = self.context.params.get(service_name, 'handle') | ||
api_base_url = handle.split('@')[-1] | ||
access_token = self.context.params.get(service_name, 'access_token') | ||
if not access_token: | ||
raise RuntimeError('Authentication data not found') | ||
yield Mastodon(access_token=access_token, api_base_url=api_base_url) | ||
|
||
def upload_file(self, session, filename): | ||
with codecs.open(filename, 'r', encoding=self.encoding) as toot_file: | ||
toot = toot_file.read() | ||
media = [] | ||
while toot.startswith('media'): | ||
media_item, toot = toot.split('\n', 1) | ||
media_item = media_item.split()[1] | ||
media.append(media_item) | ||
media_ids = [] | ||
try: | ||
for media_item in media: | ||
rsp = session.media_post(media_item) | ||
media_ids.append(rsp['id']) | ||
rsp = session.status_post(status=toot, media_ids=media_ids) | ||
except Exception as ex: | ||
return False, str(ex) | ||
return True, 'OK' | ||
|
||
|
||
def register(self): | ||
import webbrowser | ||
|
||
# get user handle | ||
handle = self.context.params.get(service_name, 'handle') | ||
api_base_url = handle.split('@')[-1] | ||
# get client data | ||
client_id = self.context.params.get('mastodon', 'client_id') | ||
client_secret = self.context.params.get('mastodon', 'client_secret') | ||
if (not client_id) or (not client_secret): | ||
client_id, client_secret = Mastodon.create_app( | ||
'pywws', scopes=['write'], api_base_url=api_base_url) | ||
self.context.params.set('mastodon', 'client_id', client_id) | ||
self.context.params.set('mastodon', 'client_secret', client_secret) | ||
# create api | ||
api = Mastodon(client_id=client_id, client_secret=client_secret, | ||
api_base_url=api_base_url) | ||
# authorise | ||
auth_request_url = api.auth_request_url(scopes=['write']) | ||
if not webbrowser.open(auth_request_url, new=2, autoraise=0): | ||
print('Please use a web browser to open the following URL') | ||
print(auth_request_url) | ||
if sys.version_info[0] >= 3: | ||
input_ = input | ||
else: | ||
input_ = raw_input | ||
code = input_('Please enter the auth code shown in your web browser: ') | ||
code = code.strip() | ||
# log in | ||
access_token = api.log_in(code=code, scopes=['write']) | ||
self.context.params.set('mastodon', 'access_token', access_token) | ||
|
||
|
||
if __name__ == "__main__": | ||
sys.exit(pywws.service.main(ToService)) |