Skip to content

Commit 8d78fab

Browse files
committed
Initial commit
0 parents  commit 8d78fab

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

71 files changed

+1293
-0
lines changed

.gitattributes

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Auto detect text files and perform LF normalization
2+
* text=auto

9781484237830.jpg

34.3 KB
Loading

Contributing.md

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Contributing to Apress Source Code
2+
3+
Copyright for Apress source code belongs to the author(s). However, under fair use you are encouraged to fork and contribute minor corrections and updates for the benefit of the author(s) and other readers.
4+
5+
## How to Contribute
6+
7+
1. Make sure you have a GitHub account.
8+
2. Fork the repository for the relevant book.
9+
3. Create a new branch on which to make your change, e.g.
10+
`git checkout -b my_code_contribution`
11+
4. Commit your change. Include a commit message describing the correction. Please note that if your commit message is not clear, the correction will not be accepted.
12+
5. Submit a pull request.
13+
14+
Thank you for your contribution!

LICENSE.txt

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
Freeware License, some rights reserved
2+
3+
Copyright (c) 2018 Sathyajith Bhat
4+
5+
Permission is hereby granted, free of charge, to anyone obtaining a copy
6+
of this software and associated documentation files (the "Software"),
7+
to work with the Software within the limits of freeware distribution and fair use.
8+
This includes the rights to use, copy, and modify the Software for personal use.
9+
Users are also allowed and encouraged to submit corrections and modifications
10+
to the Software for the benefit of other users.
11+
12+
It is not allowed to reuse, modify, or redistribute the Software for
13+
commercial use in any way, or for a user’s educational materials such as books
14+
or blog articles without prior permission from the copyright holder.
15+
16+
The above copyright notice and this permission notice need to be included
17+
in all copies or substantial portions of the software.
18+
19+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
AUTHORS OR COPYRIGHT HOLDERS OR APRESS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25+
SOFTWARE.
26+
27+

README.md

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Apress Source Code
2+
3+
This repository accompanies [*Practical Docker with Python*](https://www.apress.com/9781484237830) by Sathyajith Bhat (Apress, 2018).
4+
5+
[comment]: #cover
6+
![Cover image](9781484237830.jpg)
7+
8+
Download the files as a zip using the green button, or clone the repository to your machine using Git.
9+
10+
## Releases
11+
12+
Release v1.0 corresponds to the code in the published book, without corrections or updates.
13+
14+
## Contributions
15+
16+
See the file Contributing.md for more information on how you can contribute to this repository.

docker hello world/Dockerfile

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
FROM python:3-alpine
2+
3+
LABEL author="sathyabhat"
4+
LABEL description="Dockerfile for Python script which prints Hello, Name"
5+
6+
COPY hello-world.py /app/
7+
ENV NAME=Sathya
8+
CMD python3 /app/hello-world.py
9+

docker hello world/hello-world.py

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#!/usr/bin/env python3
2+
3+
from os import getenv
4+
5+
if getenv('NAME') is None:
6+
name = 'World'
7+
else:
8+
name = getenv('NAME')
9+
10+
print("Hello, {}!".format(name))

docker subreddit fetcher/Dockerfile

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
FROM python:3-alpine
2+
3+
COPY * /apps/subredditfetcher/
4+
WORKDIR /apps/subredditfetcher/
5+
RUN ["pip", "install", "-r", "requirements.txt"]
6+
7+
ENV NBT_ACCESS_TOKEN="495637361:AAHIhiDTX1UeX17KJy0-FsMZEqEtCFYfcP8"
8+
9+
CMD ["python", "newsbot.py"]

docker subreddit fetcher/LICENSE

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

docker subreddit fetcher/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Bot which posts top submissions from a subreddit

docker subreddit fetcher/constants.py

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
__author__ = 'Sathyajith'
2+
3+
import os
4+
ERR_NO_SOURCE = 'No sources defined! Set a source using /source list, of, sub, reddits'
5+
skip_list = []
6+
sources_dict = {}
7+
8+
BOT_KEY = os.environ['NBT_ACCESS_TOKEN']
9+
API_BASE = 'https://api.telegram.org/bot'
10+
UPDATE_PERIOD = 6
11+
FALSE_RESPONSE = {"ok": False}

docker subreddit fetcher/main.py

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
from flask import Flask
2+
from newsbot import *
3+
from states import States
4+
5+
bot = Flask(__name__)
6+
7+
8+
@bot.route('/index')
9+
def index():
10+
return 'Thou shalt not pass!'
11+
12+
13+
@bot.route('/telegram-update', methods=['POST'])
14+
def telegram_update():
15+
handle_incoming_messages(States.last_updated_id)
16+
17+
18+
if __name__ == '__main__':
19+
States.last_updated_id = get_last_updated()
20+
bot.run()
21+
22+

docker subreddit fetcher/newsbot.py

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from states import States, log
2+
from telegram import handle_incoming_messages
3+
4+
5+
def get_last_updated():
6+
try:
7+
with open('last_updated.txt', 'r') as f:
8+
try:
9+
last_updated = int(f.read())
10+
except ValueError:
11+
last_updated = 0
12+
f.close()
13+
except FileNotFoundError:
14+
last_updated = 0
15+
log.debug('Last updated id: {0}'.format(last_updated))
16+
return last_updated
17+
18+
if __name__ == '__main__':
19+
20+
try:
21+
log.debug('Starting up')
22+
States.last_updated = get_last_updated()
23+
while True:
24+
handle_incoming_messages(States.last_updated)
25+
except KeyboardInterrupt:
26+
log.info('Received KeybInterrupt, exiting')

docker subreddit fetcher/reddit.py

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import praw
2+
from states import log
3+
4+
__author__ = 'Sathyajith'
5+
6+
7+
def summarize(url):
8+
log.info('Not yet implemented!')
9+
return url
10+
11+
12+
def get_latest_news(sub_reddits):
13+
log.debug('Fetching news from reddit')
14+
r = praw.Reddit(user_agent='Telegram Xiled Chippians Group')
15+
# Can change the subreddit or add more.
16+
sub_reddits = clean_up_subreddits(sub_reddits)
17+
log.debug('Fetching subreddits: {0}'.format(sub_reddits))
18+
submissions = r.get_subreddit(sub_reddits).get_top(limit=5)
19+
submission_content = ''
20+
try:
21+
for post in submissions:
22+
submission_content += summarize(post.title + ' - ' + post.url) + '\n'
23+
except praw.errors.Forbidden:
24+
log.debug('subreddit {0} is private'.format(sub_reddits))
25+
submission_content = "Sorry couldn't fetch; subreddit is private"
26+
except praw.errors.InvalidSubreddit:
27+
log.debug('Subreddit {} is invalid or doesn''t exist.'.format(sub_reddits))
28+
submission_content = "Sorry couldn't fetch; subreddit doesn't seem to exist"
29+
except praw.errors.NotFound :
30+
log.debug('Subreddit {} is invalid or doesn''t exist.'.format(sub_reddits))
31+
submission_content = "Sorry couldn't fetch; something went wrong, please do send a report to @sathyabhat"
32+
return submission_content
33+
34+
35+
def clean_up_subreddits(sub_reddits):
36+
log.debug('Got subreddits to clean: {0}'.format(sub_reddits))
37+
return sub_reddits.strip().replace(" ", "").replace(',', '+')
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
praw

docker subreddit fetcher/states.py

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import logging
2+
3+
4+
class States(object):
5+
last_updated_id = ''
6+
7+
logging.basicConfig(level=logging.INFO,
8+
format='%(levelname)s: %(asctime)s - %(funcName)s - %(message)s')
9+
10+
log = logging.getLogger('nbt')

docker subreddit fetcher/telegram.py

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import re
2+
from time import sleep
3+
import requests
4+
5+
from states import States, log
6+
from constants import *
7+
from reddit import get_latest_news
8+
9+
__author__ = 'Sathyajith'
10+
11+
12+
def get_updates(last_updated):
13+
log.debug('Checking for requests, last updated passed is: {}'.format(last_updated))
14+
sleep(UPDATE_PERIOD)
15+
response = requests.get(API_BASE + BOT_KEY + '/getUpdates', params={'offset': last_updated+1})
16+
json_response = FALSE_RESPONSE
17+
if response.status_code != 200:
18+
# wait for a bit, try again
19+
sleep(UPDATE_PERIOD*20)
20+
get_updates(last_updated)
21+
try:
22+
json_response = response.json()
23+
except ValueError:
24+
sleep(UPDATE_PERIOD*20)
25+
get_updates(last_updated)
26+
log.info('received response: {}'.format(json_response))
27+
return json_response
28+
29+
30+
def post_message(chat_id, text):
31+
log.info('posting {} to {}'.format(text, chat_id))
32+
payload = {'chat_id': chat_id, 'text': text}
33+
requests.post(API_BASE + BOT_KEY + '/sendMessage', data=payload)
34+
35+
36+
def handle_incoming_messages(last_updated):
37+
r = get_updates(last_updated)
38+
split_chat_text = []
39+
if r['ok']:
40+
for req in r['result']:
41+
chat_sender_id = req['message']['chat']['id']
42+
try:
43+
chat_text = req['message']['text']
44+
split_chat_text = chat_text.split()
45+
except KeyError:
46+
chat_text = ''
47+
split_chat_text.append(chat_text)
48+
log.debug('Looks like no chat text was detected... moving on')
49+
try:
50+
person_id = req['message']['from']['id']
51+
except KeyError:
52+
pass
53+
54+
log.info('Chat text received: {0}'.format(chat_text))
55+
r = re.search('(source+)(.*)', chat_text)
56+
57+
if (r is not None and r.group(1) == 'source'):
58+
if r.group(2):
59+
sources_dict[person_id] = r.group(2)
60+
log.debug('Sources set for {0} to {1}'.format(sources_dict[person_id], r.group(2)))
61+
post_message(person_id, 'Sources set as {0}!'.format(r.group(2)))
62+
else:
63+
post_message(person_id, 'We need a comma separated list of subreddits! No subreddit, no news :-(')
64+
if chat_text == '/stop':
65+
log.debug('Added {0} to skip list'.format(chat_sender_id))
66+
skip_list.append(chat_sender_id)
67+
post_message(chat_sender_id, "Ok, we won't send you any more messages.")
68+
69+
if chat_text in ('/start', '/help'):
70+
helptext = '''
71+
Hi! This is a News Bot which fetches news from subreddits. Use "/source" to select a subreddit source.
72+
Example "/source programming,games" fetches news from r/programming, r/games.
73+
Use "/fetch for the bot to go ahead and fetch the news. At the moment, bot will fetch total of 5 posts from all sub reddits
74+
I will have this configurable soon.
75+
'''
76+
post_message(chat_sender_id, helptext)
77+
78+
if split_chat_text[0] == '/fetch' and (person_id not in skip_list):
79+
post_message(person_id, 'Hang on, fetching your news..')
80+
try:
81+
sub_reddits = sources_dict[person_id]
82+
except KeyError:
83+
post_message(person_id, ERR_NO_SOURCE)
84+
else:
85+
summarized_news = get_latest_news(sources_dict[person_id])
86+
post_message(person_id, summarized_news)
87+
last_updated = req['update_id']
88+
with open('last_updated.txt', 'w') as f:
89+
f.write(str(last_updated))
90+
States.last_updated = last_updated
91+
log.debug(
92+
'Updated last_updated to {0}'.format(last_updated))
93+
f.close()

errata.md

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Errata for *Book Title*
2+
3+
On **page xx** [Summary of error]:
4+
5+
Details of error here. Highlight key pieces in **bold**.
6+
7+
***
8+
9+
On **page xx** [Summary of error]:
10+
11+
Details of error here. Highlight key pieces in **bold**.
12+
13+
***

multistage-exercise/.DS_Store

6 KB
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
FROM python:3
2+
COPY requirements.txt .
3+
RUN pip install -r requirements.txt
4+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
praw
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
FROM python:3 as python-base
2+
COPY requirements.txt .
3+
RUN pip install -r requirements.txt
4+
5+
FROM python:3-alpine
6+
COPY --from=python-base /root/.cache /root/.cache
7+
COPY --from=python-base requirements.txt .
8+
RUN pip install -r requirements.txt && rm -rf /root/.cache
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
praw

subreddit_fetcher_compose/.DS_Store

6 KB
Binary file not shown.

subreddit_fetcher_compose/Dockerfile

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
FROM python:3-alpine
2+
3+
COPY * /apps/subredditfetcher/
4+
WORKDIR /apps/subredditfetcher/
5+
6+
VOLUME [ "/apps/subredditfetcher" ]
7+
RUN ["pip", "install", "-r", "requirements.txt"]
8+
# RUN ["python", "one_time.py"]
9+
10+
ENV NBT_ACCESS_TOKEN="495637361:AAHIhiDTX1UeX17KJy0-FsMZEqEtCFYfcP8"
11+
12+
CMD ["python", "newsbot.py"]

subreddit_fetcher_compose/LICENSE

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

0 commit comments

Comments
 (0)