<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://www.uni-weimar.de/kunst-und-gestaltung/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=SebastianStang</id>
	<title>Medien Wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://www.uni-weimar.de/kunst-und-gestaltung/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=SebastianStang"/>
	<link rel="alternate" type="text/html" href="https://www.uni-weimar.de/kunst-und-gestaltung/wiki/Special:Contributions/SebastianStang"/>
	<updated>2026-05-31T17:18:18Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.39.6</generator>
	<entry>
		<id>https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=GMU:Bots_%27n%27_Plots/Sebastian_Stang&amp;diff=74291</id>
		<title>GMU:Bots &#039;n&#039; Plots/Sebastian Stang</title>
		<link rel="alternate" type="text/html" href="https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=GMU:Bots_%27n%27_Plots/Sebastian_Stang&amp;diff=74291"/>
		<updated>2015-10-24T09:20:41Z</updated>

		<summary type="html">&lt;p&gt;SebastianStang: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;https://twitter.com/pfftbot&lt;br /&gt;
&lt;br /&gt;
removes phase information from images&lt;br /&gt;
&lt;br /&gt;
[[Image:CSEhtXUXIAAuFvM.png]]&lt;br /&gt;
[[Image:CSEgxHFWUAA32HG.png]]&lt;br /&gt;
[[Image:CSEhNiDWwAAJtnb.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;pfft&lt;/div&gt;</summary>
		<author><name>SebastianStang</name></author>
	</entry>
	<entry>
		<id>https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=GMU:Bots_%27n%27_Plots/Sebastian_Stang&amp;diff=74290</id>
		<title>GMU:Bots &#039;n&#039; Plots/Sebastian Stang</title>
		<link rel="alternate" type="text/html" href="https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=GMU:Bots_%27n%27_Plots/Sebastian_Stang&amp;diff=74290"/>
		<updated>2015-10-24T09:20:28Z</updated>

		<summary type="html">&lt;p&gt;SebastianStang: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;https://twitter.com/pfftbot&lt;br /&gt;
&lt;br /&gt;
removes phase information from an image&lt;br /&gt;
&lt;br /&gt;
[[Image:CSEhtXUXIAAuFvM.png]]&lt;br /&gt;
[[Image:CSEgxHFWUAA32HG.png]]&lt;br /&gt;
[[Image:CSEhNiDWwAAJtnb.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;pfft&lt;/div&gt;</summary>
		<author><name>SebastianStang</name></author>
	</entry>
	<entry>
		<id>https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=GMU:Bots_%27n%27_Plots/Sebastian_Stang&amp;diff=74289</id>
		<title>GMU:Bots &#039;n&#039; Plots/Sebastian Stang</title>
		<link rel="alternate" type="text/html" href="https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=GMU:Bots_%27n%27_Plots/Sebastian_Stang&amp;diff=74289"/>
		<updated>2015-10-24T09:16:04Z</updated>

		<summary type="html">&lt;p&gt;SebastianStang: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;https://twitter.com/pfftbot&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;pfft&lt;br /&gt;
&lt;br /&gt;
[[Image:CSEhtXUXIAAuFvM.png]]&lt;br /&gt;
[[Image:CSEgxHFWUAA32HG.png]]&lt;br /&gt;
[[Image:CSEhNiDWwAAJtnb.png]]&lt;/div&gt;</summary>
		<author><name>SebastianStang</name></author>
	</entry>
	<entry>
		<id>https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=File:CSEhtXUXIAAuFvM.png&amp;diff=74288</id>
		<title>File:CSEhtXUXIAAuFvM.png</title>
		<link rel="alternate" type="text/html" href="https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=File:CSEhtXUXIAAuFvM.png&amp;diff=74288"/>
		<updated>2015-10-24T09:15:50Z</updated>

		<summary type="html">&lt;p&gt;SebastianStang: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
&lt;br /&gt;
== Copyright status: ==&lt;br /&gt;
&lt;br /&gt;
== Source: ==&lt;/div&gt;</summary>
		<author><name>SebastianStang</name></author>
	</entry>
	<entry>
		<id>https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=GMU:Bots_%27n%27_Plots/Sebastian_Stang&amp;diff=74287</id>
		<title>GMU:Bots &#039;n&#039; Plots/Sebastian Stang</title>
		<link rel="alternate" type="text/html" href="https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=GMU:Bots_%27n%27_Plots/Sebastian_Stang&amp;diff=74287"/>
		<updated>2015-10-24T09:15:27Z</updated>

		<summary type="html">&lt;p&gt;SebastianStang: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;https://twitter.com/pfftbot&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;pfft&lt;br /&gt;
&lt;br /&gt;
[[Image:CSEgxHFWUAA32HG.png]]&lt;br /&gt;
[[Image:CSEhNiDWwAAJtnb.png]]&lt;/div&gt;</summary>
		<author><name>SebastianStang</name></author>
	</entry>
	<entry>
		<id>https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=File:CSEhNiDWwAAJtnb.png&amp;diff=74286</id>
		<title>File:CSEhNiDWwAAJtnb.png</title>
		<link rel="alternate" type="text/html" href="https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=File:CSEhNiDWwAAJtnb.png&amp;diff=74286"/>
		<updated>2015-10-24T09:14:48Z</updated>

		<summary type="html">&lt;p&gt;SebastianStang: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
&lt;br /&gt;
== Copyright status: ==&lt;br /&gt;
&lt;br /&gt;
== Source: ==&lt;/div&gt;</summary>
		<author><name>SebastianStang</name></author>
	</entry>
	<entry>
		<id>https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=GMU:Bots_%27n%27_Plots/Sebastian_Stang&amp;diff=74285</id>
		<title>GMU:Bots &#039;n&#039; Plots/Sebastian Stang</title>
		<link rel="alternate" type="text/html" href="https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=GMU:Bots_%27n%27_Plots/Sebastian_Stang&amp;diff=74285"/>
		<updated>2015-10-24T09:14:35Z</updated>

		<summary type="html">&lt;p&gt;SebastianStang: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;https://twitter.com/pfftbot&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;pfft&lt;br /&gt;
&lt;br /&gt;
[[Image:CSEgxHFWUAA32HG.png]]&lt;/div&gt;</summary>
		<author><name>SebastianStang</name></author>
	</entry>
	<entry>
		<id>https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=File:CSEgxHFWUAA32HG.png&amp;diff=74284</id>
		<title>File:CSEgxHFWUAA32HG.png</title>
		<link rel="alternate" type="text/html" href="https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=File:CSEgxHFWUAA32HG.png&amp;diff=74284"/>
		<updated>2015-10-24T09:12:38Z</updated>

		<summary type="html">&lt;p&gt;SebastianStang: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
&lt;br /&gt;
== Copyright status: ==&lt;br /&gt;
&lt;br /&gt;
== Source: ==&lt;/div&gt;</summary>
		<author><name>SebastianStang</name></author>
	</entry>
	<entry>
		<id>https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=GMU:Bots_%27n%27_Plots/Sebastian_Stang&amp;diff=74283</id>
		<title>GMU:Bots &#039;n&#039; Plots/Sebastian Stang</title>
		<link rel="alternate" type="text/html" href="https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=GMU:Bots_%27n%27_Plots/Sebastian_Stang&amp;diff=74283"/>
		<updated>2015-10-24T09:11:20Z</updated>

		<summary type="html">&lt;p&gt;SebastianStang: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt;pfft&lt;/div&gt;</summary>
		<author><name>SebastianStang</name></author>
	</entry>
	<entry>
		<id>https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=GMU:Bots_%27n%27_Plots/Sebastian_Stang&amp;diff=74282</id>
		<title>GMU:Bots &#039;n&#039; Plots/Sebastian Stang</title>
		<link rel="alternate" type="text/html" href="https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=GMU:Bots_%27n%27_Plots/Sebastian_Stang&amp;diff=74282"/>
		<updated>2015-10-24T09:10:06Z</updated>

		<summary type="html">&lt;p&gt;SebastianStang: Replaced content with &amp;quot;( )   ( )&amp;lt;br/&amp;gt;
(&amp;gt;•.•&amp;lt;)&amp;lt;br/&amp;gt;
&amp;lt;nowiki&amp;gt;  (&amp;quot;)    (&amp;quot;)&amp;lt;/nowiki&amp;gt;&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;( )   ( )&amp;lt;br/&amp;gt;&lt;br /&gt;
(&amp;gt;•.•&amp;lt;)&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;nowiki&amp;gt;  (&amp;quot;)    (&amp;quot;)&amp;lt;/nowiki&amp;gt;&lt;/div&gt;</summary>
		<author><name>SebastianStang</name></author>
	</entry>
	<entry>
		<id>https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=GMU:Bots_%27n%27_Plots/Sebastian_Stang&amp;diff=72831</id>
		<title>GMU:Bots &#039;n&#039; Plots/Sebastian Stang</title>
		<link rel="alternate" type="text/html" href="https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=GMU:Bots_%27n%27_Plots/Sebastian_Stang&amp;diff=72831"/>
		<updated>2015-06-17T16:07:42Z</updated>

		<summary type="html">&lt;p&gt;SebastianStang: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;( )   ( )&amp;lt;br/&amp;gt;&lt;br /&gt;
(&amp;gt;•.•&amp;lt;)&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;nowiki&amp;gt;  (&amp;quot;)    (&amp;quot;)&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== @_pil_bot_ ==&lt;br /&gt;
&lt;br /&gt;
compiles and runs code&lt;br /&gt;
&lt;br /&gt;
https://twitter.com/_pil_bot_&lt;br /&gt;
&lt;br /&gt;
[[File:Pilbot1.png|480px|thumb|left]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Pilbot2.png|480px|thumb|left]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr  style=&amp;quot;clear:both&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== pilbot.py ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
# -*- coding: utf-8 -*- #&lt;br /&gt;
&lt;br /&gt;
from twitterbot import TwitterBot&lt;br /&gt;
import keys&lt;br /&gt;
&lt;br /&gt;
from PIL import Image&lt;br /&gt;
from sys import argv&lt;br /&gt;
from random import random&lt;br /&gt;
&lt;br /&gt;
from pileval import PILEval&lt;br /&gt;
&lt;br /&gt;
class PilBot(TwitterBot):&lt;br /&gt;
    &lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        TwitterBot.__init__(self)&lt;br /&gt;
        self.pillermann = PILEval()&lt;br /&gt;
        &lt;br /&gt;
    def bot_init(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Initialize and configure the bot &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
        ############################&lt;br /&gt;
        # REQUIRED: LOGIN DETAILS! #&lt;br /&gt;
        ############################&lt;br /&gt;
        self.config[&#039;api_key&#039;] = keys.consumer_key  &lt;br /&gt;
        self.config[&#039;api_secret&#039;] = keys.consumer_secret&lt;br /&gt;
        self.config[&#039;access_key&#039;] = keys.access_token&lt;br /&gt;
        self.config[&#039;access_secret&#039;] = keys.access_token_secret&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        ######################################&lt;br /&gt;
        # SEMI-OPTIONAL: OTHER CONFIG STUFF! #&lt;br /&gt;
        ######################################&lt;br /&gt;
&lt;br /&gt;
        # how often to tweet, in seconds&lt;br /&gt;
        self.config[&#039;tweet_interval&#039;] = 1 * 5     # default: 1 minutes&lt;br /&gt;
&lt;br /&gt;
        # use this to define a (min, max) random range of how often to tweet&lt;br /&gt;
        # e.g., self.config[&#039;tweet_interval_range&#039;] = (5*60, 10*60) # tweets every 5-10 minutes&lt;br /&gt;
        self.config[&#039;tweet_interval_range&#039;] = None&lt;br /&gt;
&lt;br /&gt;
        # only reply to tweets that specifically mention the bot&lt;br /&gt;
        self.config[&#039;reply_direct_mention_only&#039;] = True&lt;br /&gt;
&lt;br /&gt;
        # only include bot followers (and original tweeter) in @-replies&lt;br /&gt;
        self.config[&#039;reply_followers_only&#039;] = False&lt;br /&gt;
&lt;br /&gt;
        # fav any tweets that mention this bot?&lt;br /&gt;
        self.config[&#039;autofav_mentions&#039;] = False&lt;br /&gt;
&lt;br /&gt;
        # fav any tweets containing these keywords?&lt;br /&gt;
        self.config[&#039;autofav_keywords&#039;] = []&lt;br /&gt;
&lt;br /&gt;
        # follow back all followers?&lt;br /&gt;
        self.config[&#039;autofollow&#039;] = False&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def on_scheduled_tweet(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Make a public tweet to the bot&#039;s own timeline. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        # We might take senteces from somewhere and tweet them on a regular basis ...&lt;br /&gt;
        pass # don&#039;t do anything here ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def on_mention(self, tweet, prefix):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Actions to take when a mention is received. &amp;quot;&amp;quot;&amp;quot;     &lt;br /&gt;
        text = tweet.text.split()[1]&lt;br /&gt;
        self.action(text)&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
    def on_timeline(self, tweet, prefix):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Actions to take on a timeline tweet. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        pass&lt;br /&gt;
    &lt;br /&gt;
    def action(self, code):&lt;br /&gt;
        result = self.pillermann.eval(code)&lt;br /&gt;
        &lt;br /&gt;
        if result: # post the image&lt;br /&gt;
            self.post_tweet(code, media=&amp;quot;img.png&amp;quot;, file=self.pillermann.get_file())        &lt;br /&gt;
        else: # post the error&lt;br /&gt;
            self.post_tweet(self.pillermann.last_error)        &lt;br /&gt;
    &lt;br /&gt;
if __name__ == &#039;__main__&#039;:&lt;br /&gt;
&lt;br /&gt;
    bot = PilBot()&lt;br /&gt;
    &lt;br /&gt;
    if len(argv) == 1:&lt;br /&gt;
        bot.run()&lt;br /&gt;
    else:     &lt;br /&gt;
        tweet_text = &amp;quot;text((300, 400), &#039;botsNplots&#039;, fill=&#039;#ff0&#039;)&amp;quot;        &lt;br /&gt;
        bot.action(tweet_text)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== pileval.py ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
from PIL import Image, ImageDraw&lt;br /&gt;
&lt;br /&gt;
from os import path&lt;br /&gt;
from io import BytesIO&lt;br /&gt;
&lt;br /&gt;
class PILEval():&lt;br /&gt;
    &lt;br /&gt;
    def __init__(self, filename=&#039;test.png&#039;):&lt;br /&gt;
        self.filename = filename&lt;br /&gt;
        try:&lt;br /&gt;
            self.image = Image.open(filename)&lt;br /&gt;
        except FileNotFoundError as e:&lt;br /&gt;
            self.image = Image.new(&amp;quot;RGBA&amp;quot;, (640, 480), &#039;white&#039;)        &lt;br /&gt;
        self.last_error = &#039;&#039;&lt;br /&gt;
        &lt;br /&gt;
    def __repr__(self):&lt;br /&gt;
        return &amp;quot;&amp;lt;PILEval: %s&amp;gt;&amp;quot; % self.filename&lt;br /&gt;
            &lt;br /&gt;
    def eval(self, code):&lt;br /&gt;
        draw = ImageDraw.Draw(self.image, &amp;quot;RGBA&amp;quot;)        &lt;br /&gt;
        code = &amp;quot;draw.%s&amp;quot; % code&lt;br /&gt;
&lt;br /&gt;
        try:&lt;br /&gt;
            code_obj = compile(code, &#039;&amp;lt;string&amp;gt;&#039;, &#039;exec&#039;)&lt;br /&gt;
            exec(code_obj)&lt;br /&gt;
        except Exception as e:&lt;br /&gt;
            self.last_error = str(e)&lt;br /&gt;
            return False            &lt;br /&gt;
        &lt;br /&gt;
        self.image.save(self.filename)&lt;br /&gt;
   &lt;br /&gt;
        return True&lt;br /&gt;
    &lt;br /&gt;
    def get_file(self):&lt;br /&gt;
        img = Image.open(self.filename)&lt;br /&gt;
        file = BytesIO()&lt;br /&gt;
        img.save(file, format=&#039;PNG&#039;)&lt;br /&gt;
        return file&lt;br /&gt;
        &lt;br /&gt;
if __name__ == &#039;__main__&#039;:&lt;br /&gt;
    # some q&#039;n&#039;dirty tests&lt;br /&gt;
    print(&amp;quot;main&amp;quot;)&lt;br /&gt;
    pillermann = PILEval()&lt;br /&gt;
&lt;br /&gt;
    # will work&lt;br /&gt;
    # result = pillermann.eval(&amp;quot;rectangle([20, 10, 120, 200], fill=&#039;#f00&#039;)&amp;quot;)&lt;br /&gt;
    # if result:&lt;br /&gt;
    #     print(&#039;good&#039;)&lt;br /&gt;
        &lt;br /&gt;
    # will also work&lt;br /&gt;
    # result = pillermann.eval(&amp;quot;ellipse([200, 50, 500, 500], fill=&#039;#def&#039;)&amp;quot;)&lt;br /&gt;
    # result = pillermann.eval(&amp;quot;point((123, 234), fill=&#039;#345&#039;)&amp;quot;)&lt;br /&gt;
    # result = pillermann.eval(&amp;quot;line([23, 34, 345, 456], fill=&#039;#eca&#039;, width=10)&amp;quot;)&lt;br /&gt;
    # result = pillermann.eval(&amp;quot;arc([500, 10, 600, 440], 10, 95, fill=&#039;#3f9&#039;)&amp;quot;)&lt;br /&gt;
    # result = pillermann.eval(&amp;quot;text((22, 202), &#039;_pil_bot_!&#039;, fill=&#039;#a4f&#039;)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
    result = pillermann.eval(&amp;quot;text((100, 100), text=code, fill=&#039;#000&#039;)&amp;quot;)&lt;br /&gt;
    if result:&lt;br /&gt;
        print(&amp;quot;ok&amp;quot;)&lt;br /&gt;
    else:&lt;br /&gt;
        print(pillermann.last_error)&lt;br /&gt;
    # will yield a compiler error&lt;br /&gt;
    # result = pillermann.eval(&amp;quot;rectangle([20, 10, 120, 200], fill=&#039;#f00&#039;&amp;quot;)&lt;br /&gt;
    # if not result:&lt;br /&gt;
    #     print(&amp;quot;error result: %s&amp;quot; % pillermann.last_error)      &lt;br /&gt;
&lt;br /&gt;
    # f = pillermann.get_file()&lt;br /&gt;
    # print(f)&lt;br /&gt;
    &lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== twitterbot/bot.py === &lt;br /&gt;
&lt;br /&gt;
adapts thricedotted&#039;s (http://lczzz.me) twitterbot for python 3&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
import os&lt;br /&gt;
import codecs&lt;br /&gt;
import json&lt;br /&gt;
import logging&lt;br /&gt;
import tweepy&lt;br /&gt;
import time&lt;br /&gt;
import re&lt;br /&gt;
import random&lt;br /&gt;
import pickle&lt;br /&gt;
&lt;br /&gt;
def ignore(method):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Use the @ignore decorator on TwitterBot methods you wish to leave&lt;br /&gt;
    unimplemented, such as on_timeline and on_mention.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    method.not_implemented = True&lt;br /&gt;
    return method&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
class TwitterBot:&lt;br /&gt;
&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        self.config = {}&lt;br /&gt;
&lt;br /&gt;
        self.custom_handlers = []&lt;br /&gt;
&lt;br /&gt;
        self.config[&#039;reply_direct_mention_only&#039;] = False&lt;br /&gt;
        self.config[&#039;reply_followers_only&#039;] = True&lt;br /&gt;
&lt;br /&gt;
        self.config[&#039;autofav_mentions&#039;] = False&lt;br /&gt;
        self.config[&#039;autofav_keywords&#039;] = []&lt;br /&gt;
&lt;br /&gt;
        self.config[&#039;autofollow&#039;] = False&lt;br /&gt;
&lt;br /&gt;
        self.config[&#039;tweet_interval&#039;] = 30 * 60&lt;br /&gt;
        self.config[&#039;tweet_interval_range&#039;] = None&lt;br /&gt;
&lt;br /&gt;
        self.config[&#039;reply_interval&#039;] = 10&lt;br /&gt;
        self.config[&#039;reply_interval_range&#039;] = None&lt;br /&gt;
&lt;br /&gt;
        self.config[&#039;ignore_timeline_mentions&#039;] = True&lt;br /&gt;
&lt;br /&gt;
        self.config[&#039;logging_level&#039;] = logging.DEBUG&lt;br /&gt;
        self.config[&#039;storage&#039;] = FileStorage()&lt;br /&gt;
&lt;br /&gt;
        self.state = {}&lt;br /&gt;
&lt;br /&gt;
        # call the custom initialization&lt;br /&gt;
        self.bot_init()&lt;br /&gt;
&lt;br /&gt;
        auth = tweepy.OAuthHandler(self.config[&#039;api_key&#039;], self.config[&#039;api_secret&#039;])&lt;br /&gt;
        auth.set_access_token(self.config[&#039;access_key&#039;], self.config[&#039;access_secret&#039;])&lt;br /&gt;
        self.api = tweepy.API(auth)&lt;br /&gt;
&lt;br /&gt;
        self.id = self.api.me().id&lt;br /&gt;
        self.screen_name = self.api.me().screen_name&lt;br /&gt;
&lt;br /&gt;
        logging.basicConfig(format=&#039;%(asctime)s | %(levelname)s: %(message)s&#039;, datefmt=&#039;%m/%d/%Y %I:%M:%S %p&#039;, &lt;br /&gt;
            filename=self.screen_name + &#039;.log&#039;,&lt;br /&gt;
            level=self.config[&#039;logging_level&#039;])&lt;br /&gt;
&lt;br /&gt;
        logging.info(&#039;Initializing bot...&#039;)&lt;br /&gt;
&lt;br /&gt;
        try:&lt;br /&gt;
            with self.config[&#039;storage&#039;].read(self.screen_name) as f:                &lt;br /&gt;
                self.state = pickle.load(f)&lt;br /&gt;
                &lt;br /&gt;
        except IOError:&lt;br /&gt;
            self.state[&#039;last_timeline_id&#039;] = 1&lt;br /&gt;
            self.state[&#039;last_mention_id&#039;] = 1&lt;br /&gt;
&lt;br /&gt;
            self.state[&#039;last_timeline_time&#039;] = 0&lt;br /&gt;
            self.state[&#039;last_mention_time&#039;] = 0&lt;br /&gt;
&lt;br /&gt;
            self.state[&#039;last_tweet_id&#039;] = 1&lt;br /&gt;
            self.state[&#039;last_tweet_time&#039;] = 1&lt;br /&gt;
&lt;br /&gt;
            self.state[&#039;last_reply_id&#039;] = 0&lt;br /&gt;
            self.state[&#039;last_reply_time&#039;] = 0&lt;br /&gt;
&lt;br /&gt;
            self.state[&#039;recent_timeline&#039;] = []&lt;br /&gt;
            self.state[&#039;mention_queue&#039;] = []&lt;br /&gt;
&lt;br /&gt;
        self.state[&#039;friends&#039;] = self.api.friends_ids(self.id)&lt;br /&gt;
        self.state[&#039;followers&#039;] = self.api.followers_ids(self.id)&lt;br /&gt;
        self.state[&#039;new_followers&#039;] = []&lt;br /&gt;
        self.state[&#039;last_follow_check&#039;] = 0&lt;br /&gt;
&lt;br /&gt;
        logging.info(&#039;Bot initialized!&#039;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def bot_init(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Initialize custom state values for your bot.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        raise NotImplementedError(&amp;quot;You MUST have bot_init() implemented in your bot! What have you DONE!&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def log(self, message, level=logging.INFO):&lt;br /&gt;
        if level == logging.ERROR:&lt;br /&gt;
            logging.error(message)&lt;br /&gt;
        else:&lt;br /&gt;
            logging.info(message)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _log_tweepy_error(self, message, e):&lt;br /&gt;
        try:&lt;br /&gt;
            e_message = e.message[0][&#039;message&#039;]&lt;br /&gt;
            code = e.message[0][&#039;code&#039;]&lt;br /&gt;
            self.log(&amp;quot;{}: {} ({})&amp;quot;.format(message, e_message, code), level=logging.ERROR)&lt;br /&gt;
        except:&lt;br /&gt;
            self.log(message, e)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _tweet_url(self, tweet):&lt;br /&gt;
        return &amp;quot;http://twitter.com/&amp;quot; + tweet.author.screen_name + &amp;quot;/status/&amp;quot; + str(tweet.id)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _save_state(self):&lt;br /&gt;
        with self.config[&#039;storage&#039;].write(self.screen_name) as f:&lt;br /&gt;
            pickle.dump(self.state, f)&lt;br /&gt;
            self.log(&#039;Bot state saved&#039;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def on_scheduled_tweet(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Post a general tweet to own timeline.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        #self.post_tweet(text)&lt;br /&gt;
        raise NotImplementedError(&amp;quot;You need to implement this to tweet to timeline (or pass if you don&#039;t want to)!&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def on_mention(self, tweet, prefix):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Perform some action upon receiving a mention.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        #self.post_tweet(text)&lt;br /&gt;
        raise NotImplementedError(&amp;quot;You need to implement this to reply to/fav mentions (or pass if you don&#039;t want to)!&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def on_timeline(self, tweet, prefix):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Perform some action on a tweet on the timeline.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        #self.post_tweet(text)&lt;br /&gt;
        raise NotImplementedError(&amp;quot;You need to implement this to reply to/fav timeline tweets (or pass if you don&#039;t want to)!&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def on_follow(self, f_id):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Perform some action when followed.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        if self.config[&#039;autofollow&#039;]:&lt;br /&gt;
            try:&lt;br /&gt;
                self.api.create_friendship(f_id, follow=True)&lt;br /&gt;
                self.state[&#039;friends&#039;].append(f_id)&lt;br /&gt;
                logging.info(&#039;Followed user id {}&#039;.format(f_id))&lt;br /&gt;
            except tweepy.TweepError as e:&lt;br /&gt;
                self._log_tweepy_error(&#039;Unable to follow user&#039;, e)&lt;br /&gt;
&lt;br /&gt;
            time.sleep(3)&lt;br /&gt;
&lt;br /&gt;
        self.state[&#039;followers&#039;].append(f_id)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def post_tweet(self, text, reply_to=None, media=None, file=None):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Post a tweet containing the text. &lt;br /&gt;
        If you provide a filename the file will be added to the tweet.&lt;br /&gt;
        You can also provide the filedata, for example when using dynamically created images.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
        kwargs = { &amp;quot;status&amp;quot;: text }&lt;br /&gt;
        args = []&lt;br /&gt;
&lt;br /&gt;
        if media is not None:&lt;br /&gt;
            cmd = self.api.update_with_media&lt;br /&gt;
            args.insert(0, media)&lt;br /&gt;
&lt;br /&gt;
            if file is not None:&lt;br /&gt;
                kwargs[&amp;quot;file&amp;quot;] = file&lt;br /&gt;
        else:&lt;br /&gt;
            cmd = self.api.update_status&lt;br /&gt;
&lt;br /&gt;
        try:&lt;br /&gt;
            self.log(&#039;Tweeting &amp;quot;{}&amp;quot;&#039;.format(text))&lt;br /&gt;
            if reply_to:&lt;br /&gt;
                self.log(&amp;quot;-- Responding to status {}&amp;quot;.format(self._tweet_url(reply_to)))&lt;br /&gt;
                kwargs[&#039;in_reply_to_status_id&#039;] = reply_to.id&lt;br /&gt;
            else:&lt;br /&gt;
                self.log(&amp;quot;-- Posting to own timeline&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
            tweet = cmd(*args, **kwargs)&lt;br /&gt;
            self.log(&#039;Status posted at {}&#039;.format(self._tweet_url(tweet)))&lt;br /&gt;
            return True&lt;br /&gt;
&lt;br /&gt;
        except tweepy.TweepError as e:&lt;br /&gt;
            self._log_tweepy_error(&#039;Can\&#039;t post status&#039;, e)&lt;br /&gt;
            return False&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def favorite_tweet(self, tweet):&lt;br /&gt;
        try:&lt;br /&gt;
            logging.info(&#039;Faving &#039; + self._tweet_url(tweet))&lt;br /&gt;
            self.api.create_favorite(tweet.id)&lt;br /&gt;
&lt;br /&gt;
        except tweepy.TweepError as e:&lt;br /&gt;
            self._log_tweepy_error(&#039;Can\&#039;t fav status&#039;, e)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _ignore_method(self, method):&lt;br /&gt;
        return hasattr(method, &#039;not_implemented&#039;) and method.not_implemented&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _handle_timeline(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Reads the latest tweets in the bots timeline and perform some action.&lt;br /&gt;
        self.recent_timeline&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        for tweet in self.state[&#039;recent_timeline&#039;]:&lt;br /&gt;
            prefix = self.get_mention_prefix(tweet)&lt;br /&gt;
            self.on_timeline(tweet, prefix)&lt;br /&gt;
&lt;br /&gt;
            words = tweet.text.lower().split()&lt;br /&gt;
            if any(w in words for w in self.config[&#039;autofav_keywords&#039;]):&lt;br /&gt;
                self.favorite_tweet(tweet)&lt;br /&gt;
&lt;br /&gt;
            #time.sleep(self.config[&#039;reply_interval&#039;])&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _handle_mentions(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Performs some action on the mentions in self.mention_queue&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        # TODO: only handle a certain number of mentions at a time?&lt;br /&gt;
        for mention in iter(self.state[&#039;mention_queue&#039;]):&lt;br /&gt;
            prefix = self.get_mention_prefix(mention)&lt;br /&gt;
            self.on_mention(mention, prefix)&lt;br /&gt;
            self.state[&#039;mention_queue&#039;].remove(mention)&lt;br /&gt;
&lt;br /&gt;
            if self.config[&#039;autofav_mentions&#039;]:&lt;br /&gt;
                self.favorite_tweet(mention)&lt;br /&gt;
&lt;br /&gt;
            #time.sleep(self.config[&#039;reply_interval&#039;])&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def get_mention_prefix(self, tweet):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Returns a string of users to @-mention when responding to a tweet.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        mention_back = [&#039;@&#039; + tweet.author.screen_name]&lt;br /&gt;
        mention_back += [s for s in re.split(&#039;[^@\w]&#039;, tweet.text) if len(s) &amp;gt; 2 and s[0] == &#039;@&#039; and s[1:] != self.screen_name]&lt;br /&gt;
&lt;br /&gt;
        if self.config[&#039;reply_followers_only&#039;]:&lt;br /&gt;
            mention_back = [s for s in mention_back if s[1:] in self.state[&#039;followers&#039;] or s == &#039;@&#039; + tweet.author.screen_name]&lt;br /&gt;
&lt;br /&gt;
        return &#039; &#039;.join(mention_back)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _check_mentions(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Checks mentions and loads most recent tweets into the mention queue&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        if self._ignore_method(self.on_mention):&lt;br /&gt;
            logging.debug(&#039;Ignoring mentions&#039;)&lt;br /&gt;
            return&lt;br /&gt;
&lt;br /&gt;
        try:&lt;br /&gt;
            current_mentions = self.api.mentions_timeline(since_id=self.state[&#039;last_mention_id&#039;], count=100)&lt;br /&gt;
&lt;br /&gt;
            # direct mentions only?&lt;br /&gt;
            if self.config[&#039;reply_direct_mention_only&#039;]:&lt;br /&gt;
                current_mentions = [t for t in current_mentions if re.split(&#039;[^@\w]&#039;, t.text)[0] == &#039;@&#039; + self.screen_name]&lt;br /&gt;
&lt;br /&gt;
            if len(current_mentions) != 0:&lt;br /&gt;
                self.state[&#039;last_mention_id&#039;] = current_mentions[0].id&lt;br /&gt;
            &lt;br /&gt;
            self.state[&#039;last_mention_time&#039;] = time.time()&lt;br /&gt;
&lt;br /&gt;
            self.state[&#039;mention_queue&#039;] += reversed(current_mentions)&lt;br /&gt;
&lt;br /&gt;
            logging.info(&#039;Mentions updated ({} retrieved, {} total in queue)&#039;.format(len(current_mentions), len(self.state[&#039;mention_queue&#039;])))&lt;br /&gt;
&lt;br /&gt;
        except tweepy.TweepError as e:&lt;br /&gt;
            self._log_tweepy_error(&#039;Can\&#039;t retrieve mentions&#039;, e)&lt;br /&gt;
&lt;br /&gt;
        except IncompleteRead as e:&lt;br /&gt;
            self.log(&#039;Incomplete read error -- skipping mentions update&#039;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _check_timeline(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Checks timeline and loads most recent tweets into recent timeline&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        if self._ignore_method(self.on_timeline):&lt;br /&gt;
            logging.debug(&#039;Ignoring timeline&#039;)&lt;br /&gt;
            return&lt;br /&gt;
&lt;br /&gt;
        try:&lt;br /&gt;
            current_timeline = self.api.home_timeline(count=200, since_id=self.state[&#039;last_timeline_id&#039;])&lt;br /&gt;
&lt;br /&gt;
            # remove my tweets&lt;br /&gt;
            current_timeline = [t for t in current_timeline if t.author.screen_name.lower() != self.screen_name.lower()]&lt;br /&gt;
&lt;br /&gt;
            # remove all tweets mentioning me&lt;br /&gt;
            current_timeline = [t for t in current_timeline if not re.search(&#039;@&#039;+self.screen_name, t.text, flags=re.IGNORECASE)]&lt;br /&gt;
&lt;br /&gt;
            if self.config[&#039;ignore_timeline_mentions&#039;]:&lt;br /&gt;
                # remove all tweets with mentions (heuristically)&lt;br /&gt;
                current_timeline = [t for t in current_timeline if &#039;@&#039; not in t.text]&lt;br /&gt;
&lt;br /&gt;
            if len(current_timeline) != 0:&lt;br /&gt;
                self.state[&#039;last_timeline_id&#039;] = current_timeline[0].id&lt;br /&gt;
            &lt;br /&gt;
            self.state[&#039;last_timeline_time&#039;] = time.time()&lt;br /&gt;
&lt;br /&gt;
            self.state[&#039;recent_timeline&#039;] = list(reversed(current_timeline))&lt;br /&gt;
&lt;br /&gt;
            logging.info(&#039;Timeline updated ({} retrieved)&#039;.format(len(current_timeline)))&lt;br /&gt;
&lt;br /&gt;
        except tweepy.TweepError as e:&lt;br /&gt;
            self._log_tweepy_error(&#039;Can\&#039;t retrieve timeline&#039;, e)&lt;br /&gt;
&lt;br /&gt;
        except IncompleteRead as e:&lt;br /&gt;
            self.log(&#039;Incomplete read error -- skipping timeline update&#039;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _check_followers(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Checks followers.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        logging.info(&amp;quot;Checking for new followers...&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
        try:&lt;br /&gt;
            self.state[&#039;new_followers&#039;] = [f_id for f_id in self.api.followers_ids(self.id) if f_id not in self.state[&#039;followers&#039;]]&lt;br /&gt;
&lt;br /&gt;
            self.config[&#039;last_follow_check&#039;] = time.time()&lt;br /&gt;
&lt;br /&gt;
        except tweepy.TweepError as e:&lt;br /&gt;
            self._log_tweepy_error(&#039;Can\&#039;t update followers&#039;, e)&lt;br /&gt;
&lt;br /&gt;
        except IncompleteRead as e:&lt;br /&gt;
            self.log(&#039;Incomplete read error -- skipping followers update&#039;)&lt;br /&gt;
&lt;br /&gt;
            &lt;br /&gt;
    def _handle_followers(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Handles new followers.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        for f_id in self.state[&#039;new_followers&#039;]:&lt;br /&gt;
            self.on_follow(f_id)&lt;br /&gt;
&lt;br /&gt;
    def register_custom_handler(self, action, interval):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Register a custom action to run at some interval.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        handler = {}&lt;br /&gt;
&lt;br /&gt;
        handler[&#039;action&#039;] = action&lt;br /&gt;
        handler[&#039;interval&#039;] = interval&lt;br /&gt;
        handler[&#039;last_run&#039;] = 0&lt;br /&gt;
&lt;br /&gt;
        self.custom_handlers.append(handler)&lt;br /&gt;
&lt;br /&gt;
    def run(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Runs the bot! This probably shouldn&#039;t be in a &amp;quot;while True&amp;quot; lol.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        while True:&lt;br /&gt;
            &lt;br /&gt;
            # check followers every 15 minutes&lt;br /&gt;
            #if self.autofollow and (time.time() - self.last_follow_check) &amp;gt; (15 * 60): &lt;br /&gt;
            if self.state[&#039;last_follow_check&#039;] &amp;gt; (15 * 60): &lt;br /&gt;
                self._check_followers()&lt;br /&gt;
                self._handle_followers()&lt;br /&gt;
&lt;br /&gt;
            # check mentions every minute-ish&lt;br /&gt;
            #if self.reply_to_mentions and (time.time() - self.last_mention_time) &amp;gt; 60:&lt;br /&gt;
            if (time.time() - self.state[&#039;last_mention_time&#039;]) &amp;gt; 60:&lt;br /&gt;
                self._check_mentions()&lt;br /&gt;
                self._handle_mentions()&lt;br /&gt;
&lt;br /&gt;
            # tweet to timeline&lt;br /&gt;
            #if self.reply_to_timeline and (time.time() - self.last_mention_time) &amp;gt; 60:&lt;br /&gt;
            if (time.time() - self.state[&#039;last_timeline_time&#039;]) &amp;gt; 60:&lt;br /&gt;
                self._check_timeline()&lt;br /&gt;
                self._handle_timeline()&lt;br /&gt;
&lt;br /&gt;
            # tweet to timeline on the correct interval&lt;br /&gt;
            if (time.time() - self.state[&#039;last_tweet_time&#039;]) &amp;gt; self.config[&#039;tweet_interval&#039;]:&lt;br /&gt;
                self.on_scheduled_tweet()&lt;br /&gt;
&lt;br /&gt;
                # TODO: maybe this should only run if the above is successful...&lt;br /&gt;
                if self.config[&#039;tweet_interval_range&#039;] is not None:&lt;br /&gt;
                    self.config[&#039;tweet_interval&#039;] = random.randint(*self.config[&#039;tweet_interval_range&#039;])&lt;br /&gt;
&lt;br /&gt;
                self.log(&amp;quot;Next tweet in {} seconds&amp;quot;.format(self.config[&#039;tweet_interval&#039;]))&lt;br /&gt;
                self.state[&#039;last_tweet_time&#039;] = time.time()&lt;br /&gt;
&lt;br /&gt;
            # run custom action&lt;br /&gt;
            for handler in self.custom_handlers:&lt;br /&gt;
                if (time.time() - handler[&#039;last_run&#039;]) &amp;gt; handler[&#039;interval&#039;]:&lt;br /&gt;
                    handler[&#039;action&#039;]()&lt;br /&gt;
                    handler[&#039;last_run&#039;] = time.time()&lt;br /&gt;
&lt;br /&gt;
            # save current state&lt;br /&gt;
            self._save_state()&lt;br /&gt;
&lt;br /&gt;
            logging.info(&amp;quot;Sleeping for a bit...&amp;quot;)&lt;br /&gt;
            time.sleep(30)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
class FileStorage(object):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Default storage adapter.&lt;br /&gt;
&lt;br /&gt;
    Adapters must implement two methods: read(name) and write(name).&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def read(self, name):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Return an IO-like object that will produce binary data when read from.&lt;br /&gt;
        If nothing is stored under the given name, raise IOError.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        filename = self._get_filename(name)&lt;br /&gt;
        if os.path.exists(filename):&lt;br /&gt;
            logging.debug(&amp;quot;Reading from {}&amp;quot;.format(filename))&lt;br /&gt;
        else:&lt;br /&gt;
            logging.debug(&amp;quot;{} doesn&#039;t exist&amp;quot;.format(filename))&lt;br /&gt;
        return open(filename, &#039;rb&#039;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def write(self, name):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Return an IO-like object that will store binary data written to it.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        filename = self._get_filename(name)&lt;br /&gt;
        if os.path.exists(filename):&lt;br /&gt;
            logging.debug(&amp;quot;Overwriting {}&amp;quot;.format(filename))&lt;br /&gt;
        else:&lt;br /&gt;
            logging.debug(&amp;quot;Creating {}&amp;quot;.format(filename))&lt;br /&gt;
        return open(filename, &#039;wb&#039;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _get_filename(self, name):&lt;br /&gt;
        return &#039;{}_state.pkl&#039;.format(name)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>SebastianStang</name></author>
	</entry>
	<entry>
		<id>https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=GMU:Bots_%27n%27_Plots/Sebastian_Stang&amp;diff=72830</id>
		<title>GMU:Bots &#039;n&#039; Plots/Sebastian Stang</title>
		<link rel="alternate" type="text/html" href="https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=GMU:Bots_%27n%27_Plots/Sebastian_Stang&amp;diff=72830"/>
		<updated>2015-06-17T16:05:59Z</updated>

		<summary type="html">&lt;p&gt;SebastianStang: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;( )   ( )&amp;lt;br/&amp;gt;&lt;br /&gt;
(&amp;gt;•.•&amp;lt;)&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;nowiki&amp;gt;  (&amp;quot;)    (&amp;quot;)&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== @_pil_bot_ ==&lt;br /&gt;
&lt;br /&gt;
https://twitter.com/_pil_bot_&lt;br /&gt;
&lt;br /&gt;
[[File:Pilbot1.png|480px|thumb|left]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Pilbot2.png|480px|thumb|left]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr  style=&amp;quot;clear:both&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== pilbot.py ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
# -*- coding: utf-8 -*- #&lt;br /&gt;
&lt;br /&gt;
from twitterbot import TwitterBot&lt;br /&gt;
import keys&lt;br /&gt;
&lt;br /&gt;
from PIL import Image&lt;br /&gt;
from sys import argv&lt;br /&gt;
from random import random&lt;br /&gt;
&lt;br /&gt;
from pileval import PILEval&lt;br /&gt;
&lt;br /&gt;
class PilBot(TwitterBot):&lt;br /&gt;
    &lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        TwitterBot.__init__(self)&lt;br /&gt;
        self.pillermann = PILEval()&lt;br /&gt;
        &lt;br /&gt;
    def bot_init(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Initialize and configure the bot &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
        ############################&lt;br /&gt;
        # REQUIRED: LOGIN DETAILS! #&lt;br /&gt;
        ############################&lt;br /&gt;
        self.config[&#039;api_key&#039;] = keys.consumer_key  &lt;br /&gt;
        self.config[&#039;api_secret&#039;] = keys.consumer_secret&lt;br /&gt;
        self.config[&#039;access_key&#039;] = keys.access_token&lt;br /&gt;
        self.config[&#039;access_secret&#039;] = keys.access_token_secret&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        ######################################&lt;br /&gt;
        # SEMI-OPTIONAL: OTHER CONFIG STUFF! #&lt;br /&gt;
        ######################################&lt;br /&gt;
&lt;br /&gt;
        # how often to tweet, in seconds&lt;br /&gt;
        self.config[&#039;tweet_interval&#039;] = 1 * 5     # default: 1 minutes&lt;br /&gt;
&lt;br /&gt;
        # use this to define a (min, max) random range of how often to tweet&lt;br /&gt;
        # e.g., self.config[&#039;tweet_interval_range&#039;] = (5*60, 10*60) # tweets every 5-10 minutes&lt;br /&gt;
        self.config[&#039;tweet_interval_range&#039;] = None&lt;br /&gt;
&lt;br /&gt;
        # only reply to tweets that specifically mention the bot&lt;br /&gt;
        self.config[&#039;reply_direct_mention_only&#039;] = True&lt;br /&gt;
&lt;br /&gt;
        # only include bot followers (and original tweeter) in @-replies&lt;br /&gt;
        self.config[&#039;reply_followers_only&#039;] = False&lt;br /&gt;
&lt;br /&gt;
        # fav any tweets that mention this bot?&lt;br /&gt;
        self.config[&#039;autofav_mentions&#039;] = False&lt;br /&gt;
&lt;br /&gt;
        # fav any tweets containing these keywords?&lt;br /&gt;
        self.config[&#039;autofav_keywords&#039;] = []&lt;br /&gt;
&lt;br /&gt;
        # follow back all followers?&lt;br /&gt;
        self.config[&#039;autofollow&#039;] = False&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def on_scheduled_tweet(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Make a public tweet to the bot&#039;s own timeline. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        # We might take senteces from somewhere and tweet them on a regular basis ...&lt;br /&gt;
        pass # don&#039;t do anything here ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def on_mention(self, tweet, prefix):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Actions to take when a mention is received. &amp;quot;&amp;quot;&amp;quot;     &lt;br /&gt;
        text = tweet.text.split()[1]&lt;br /&gt;
        self.action(text)&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
    def on_timeline(self, tweet, prefix):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Actions to take on a timeline tweet. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        pass&lt;br /&gt;
    &lt;br /&gt;
    def action(self, code):&lt;br /&gt;
        result = self.pillermann.eval(code)&lt;br /&gt;
        &lt;br /&gt;
        if result: # post the image&lt;br /&gt;
            self.post_tweet(code, media=&amp;quot;img.png&amp;quot;, file=self.pillermann.get_file())        &lt;br /&gt;
        else: # post the error&lt;br /&gt;
            self.post_tweet(self.pillermann.last_error)        &lt;br /&gt;
    &lt;br /&gt;
if __name__ == &#039;__main__&#039;:&lt;br /&gt;
&lt;br /&gt;
    bot = PilBot()&lt;br /&gt;
    &lt;br /&gt;
    if len(argv) == 1:&lt;br /&gt;
        bot.run()&lt;br /&gt;
    else:     &lt;br /&gt;
        tweet_text = &amp;quot;text((300, 400), &#039;botsNplots&#039;, fill=&#039;#ff0&#039;)&amp;quot;        &lt;br /&gt;
        bot.action(tweet_text)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== pileval.py ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
from PIL import Image, ImageDraw&lt;br /&gt;
&lt;br /&gt;
from os import path&lt;br /&gt;
from io import BytesIO&lt;br /&gt;
&lt;br /&gt;
class PILEval():&lt;br /&gt;
    &lt;br /&gt;
    def __init__(self, filename=&#039;test.png&#039;):&lt;br /&gt;
        self.filename = filename&lt;br /&gt;
        try:&lt;br /&gt;
            self.image = Image.open(filename)&lt;br /&gt;
        except FileNotFoundError as e:&lt;br /&gt;
            self.image = Image.new(&amp;quot;RGBA&amp;quot;, (640, 480), &#039;white&#039;)        &lt;br /&gt;
        self.last_error = &#039;&#039;&lt;br /&gt;
        &lt;br /&gt;
    def __repr__(self):&lt;br /&gt;
        return &amp;quot;&amp;lt;PILEval: %s&amp;gt;&amp;quot; % self.filename&lt;br /&gt;
            &lt;br /&gt;
    def eval(self, code):&lt;br /&gt;
        draw = ImageDraw.Draw(self.image, &amp;quot;RGBA&amp;quot;)        &lt;br /&gt;
        code = &amp;quot;draw.%s&amp;quot; % code&lt;br /&gt;
&lt;br /&gt;
        try:&lt;br /&gt;
            code_obj = compile(code, &#039;&amp;lt;string&amp;gt;&#039;, &#039;exec&#039;)&lt;br /&gt;
            exec(code_obj)&lt;br /&gt;
        except Exception as e:&lt;br /&gt;
            self.last_error = str(e)&lt;br /&gt;
            return False            &lt;br /&gt;
        &lt;br /&gt;
        self.image.save(self.filename)&lt;br /&gt;
   &lt;br /&gt;
        return True&lt;br /&gt;
    &lt;br /&gt;
    def get_file(self):&lt;br /&gt;
        img = Image.open(self.filename)&lt;br /&gt;
        file = BytesIO()&lt;br /&gt;
        img.save(file, format=&#039;PNG&#039;)&lt;br /&gt;
        return file&lt;br /&gt;
        &lt;br /&gt;
if __name__ == &#039;__main__&#039;:&lt;br /&gt;
    # some q&#039;n&#039;dirty tests&lt;br /&gt;
    print(&amp;quot;main&amp;quot;)&lt;br /&gt;
    pillermann = PILEval()&lt;br /&gt;
&lt;br /&gt;
    # will work&lt;br /&gt;
    # result = pillermann.eval(&amp;quot;rectangle([20, 10, 120, 200], fill=&#039;#f00&#039;)&amp;quot;)&lt;br /&gt;
    # if result:&lt;br /&gt;
    #     print(&#039;good&#039;)&lt;br /&gt;
        &lt;br /&gt;
    # will also work&lt;br /&gt;
    # result = pillermann.eval(&amp;quot;ellipse([200, 50, 500, 500], fill=&#039;#def&#039;)&amp;quot;)&lt;br /&gt;
    # result = pillermann.eval(&amp;quot;point((123, 234), fill=&#039;#345&#039;)&amp;quot;)&lt;br /&gt;
    # result = pillermann.eval(&amp;quot;line([23, 34, 345, 456], fill=&#039;#eca&#039;, width=10)&amp;quot;)&lt;br /&gt;
    # result = pillermann.eval(&amp;quot;arc([500, 10, 600, 440], 10, 95, fill=&#039;#3f9&#039;)&amp;quot;)&lt;br /&gt;
    # result = pillermann.eval(&amp;quot;text((22, 202), &#039;_pil_bot_!&#039;, fill=&#039;#a4f&#039;)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
    result = pillermann.eval(&amp;quot;text((100, 100), text=code, fill=&#039;#000&#039;)&amp;quot;)&lt;br /&gt;
    if result:&lt;br /&gt;
        print(&amp;quot;ok&amp;quot;)&lt;br /&gt;
    else:&lt;br /&gt;
        print(pillermann.last_error)&lt;br /&gt;
    # will yield a compiler error&lt;br /&gt;
    # result = pillermann.eval(&amp;quot;rectangle([20, 10, 120, 200], fill=&#039;#f00&#039;&amp;quot;)&lt;br /&gt;
    # if not result:&lt;br /&gt;
    #     print(&amp;quot;error result: %s&amp;quot; % pillermann.last_error)      &lt;br /&gt;
&lt;br /&gt;
    # f = pillermann.get_file()&lt;br /&gt;
    # print(f)&lt;br /&gt;
    &lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== twitterbot/bot.py === &lt;br /&gt;
&lt;br /&gt;
adapts thricedotted&#039;s (http://lczzz.me) twitterbot for python 3&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
import os&lt;br /&gt;
import codecs&lt;br /&gt;
import json&lt;br /&gt;
import logging&lt;br /&gt;
import tweepy&lt;br /&gt;
import time&lt;br /&gt;
import re&lt;br /&gt;
import random&lt;br /&gt;
import pickle&lt;br /&gt;
&lt;br /&gt;
def ignore(method):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Use the @ignore decorator on TwitterBot methods you wish to leave&lt;br /&gt;
    unimplemented, such as on_timeline and on_mention.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    method.not_implemented = True&lt;br /&gt;
    return method&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
class TwitterBot:&lt;br /&gt;
&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        self.config = {}&lt;br /&gt;
&lt;br /&gt;
        self.custom_handlers = []&lt;br /&gt;
&lt;br /&gt;
        self.config[&#039;reply_direct_mention_only&#039;] = False&lt;br /&gt;
        self.config[&#039;reply_followers_only&#039;] = True&lt;br /&gt;
&lt;br /&gt;
        self.config[&#039;autofav_mentions&#039;] = False&lt;br /&gt;
        self.config[&#039;autofav_keywords&#039;] = []&lt;br /&gt;
&lt;br /&gt;
        self.config[&#039;autofollow&#039;] = False&lt;br /&gt;
&lt;br /&gt;
        self.config[&#039;tweet_interval&#039;] = 30 * 60&lt;br /&gt;
        self.config[&#039;tweet_interval_range&#039;] = None&lt;br /&gt;
&lt;br /&gt;
        self.config[&#039;reply_interval&#039;] = 10&lt;br /&gt;
        self.config[&#039;reply_interval_range&#039;] = None&lt;br /&gt;
&lt;br /&gt;
        self.config[&#039;ignore_timeline_mentions&#039;] = True&lt;br /&gt;
&lt;br /&gt;
        self.config[&#039;logging_level&#039;] = logging.DEBUG&lt;br /&gt;
        self.config[&#039;storage&#039;] = FileStorage()&lt;br /&gt;
&lt;br /&gt;
        self.state = {}&lt;br /&gt;
&lt;br /&gt;
        # call the custom initialization&lt;br /&gt;
        self.bot_init()&lt;br /&gt;
&lt;br /&gt;
        auth = tweepy.OAuthHandler(self.config[&#039;api_key&#039;], self.config[&#039;api_secret&#039;])&lt;br /&gt;
        auth.set_access_token(self.config[&#039;access_key&#039;], self.config[&#039;access_secret&#039;])&lt;br /&gt;
        self.api = tweepy.API(auth)&lt;br /&gt;
&lt;br /&gt;
        self.id = self.api.me().id&lt;br /&gt;
        self.screen_name = self.api.me().screen_name&lt;br /&gt;
&lt;br /&gt;
        logging.basicConfig(format=&#039;%(asctime)s | %(levelname)s: %(message)s&#039;, datefmt=&#039;%m/%d/%Y %I:%M:%S %p&#039;, &lt;br /&gt;
            filename=self.screen_name + &#039;.log&#039;,&lt;br /&gt;
            level=self.config[&#039;logging_level&#039;])&lt;br /&gt;
&lt;br /&gt;
        logging.info(&#039;Initializing bot...&#039;)&lt;br /&gt;
&lt;br /&gt;
        try:&lt;br /&gt;
            with self.config[&#039;storage&#039;].read(self.screen_name) as f:                &lt;br /&gt;
                self.state = pickle.load(f)&lt;br /&gt;
                &lt;br /&gt;
        except IOError:&lt;br /&gt;
            self.state[&#039;last_timeline_id&#039;] = 1&lt;br /&gt;
            self.state[&#039;last_mention_id&#039;] = 1&lt;br /&gt;
&lt;br /&gt;
            self.state[&#039;last_timeline_time&#039;] = 0&lt;br /&gt;
            self.state[&#039;last_mention_time&#039;] = 0&lt;br /&gt;
&lt;br /&gt;
            self.state[&#039;last_tweet_id&#039;] = 1&lt;br /&gt;
            self.state[&#039;last_tweet_time&#039;] = 1&lt;br /&gt;
&lt;br /&gt;
            self.state[&#039;last_reply_id&#039;] = 0&lt;br /&gt;
            self.state[&#039;last_reply_time&#039;] = 0&lt;br /&gt;
&lt;br /&gt;
            self.state[&#039;recent_timeline&#039;] = []&lt;br /&gt;
            self.state[&#039;mention_queue&#039;] = []&lt;br /&gt;
&lt;br /&gt;
        self.state[&#039;friends&#039;] = self.api.friends_ids(self.id)&lt;br /&gt;
        self.state[&#039;followers&#039;] = self.api.followers_ids(self.id)&lt;br /&gt;
        self.state[&#039;new_followers&#039;] = []&lt;br /&gt;
        self.state[&#039;last_follow_check&#039;] = 0&lt;br /&gt;
&lt;br /&gt;
        logging.info(&#039;Bot initialized!&#039;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def bot_init(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Initialize custom state values for your bot.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        raise NotImplementedError(&amp;quot;You MUST have bot_init() implemented in your bot! What have you DONE!&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def log(self, message, level=logging.INFO):&lt;br /&gt;
        if level == logging.ERROR:&lt;br /&gt;
            logging.error(message)&lt;br /&gt;
        else:&lt;br /&gt;
            logging.info(message)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _log_tweepy_error(self, message, e):&lt;br /&gt;
        try:&lt;br /&gt;
            e_message = e.message[0][&#039;message&#039;]&lt;br /&gt;
            code = e.message[0][&#039;code&#039;]&lt;br /&gt;
            self.log(&amp;quot;{}: {} ({})&amp;quot;.format(message, e_message, code), level=logging.ERROR)&lt;br /&gt;
        except:&lt;br /&gt;
            self.log(message, e)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _tweet_url(self, tweet):&lt;br /&gt;
        return &amp;quot;http://twitter.com/&amp;quot; + tweet.author.screen_name + &amp;quot;/status/&amp;quot; + str(tweet.id)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _save_state(self):&lt;br /&gt;
        with self.config[&#039;storage&#039;].write(self.screen_name) as f:&lt;br /&gt;
            pickle.dump(self.state, f)&lt;br /&gt;
            self.log(&#039;Bot state saved&#039;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def on_scheduled_tweet(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Post a general tweet to own timeline.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        #self.post_tweet(text)&lt;br /&gt;
        raise NotImplementedError(&amp;quot;You need to implement this to tweet to timeline (or pass if you don&#039;t want to)!&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def on_mention(self, tweet, prefix):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Perform some action upon receiving a mention.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        #self.post_tweet(text)&lt;br /&gt;
        raise NotImplementedError(&amp;quot;You need to implement this to reply to/fav mentions (or pass if you don&#039;t want to)!&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def on_timeline(self, tweet, prefix):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Perform some action on a tweet on the timeline.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        #self.post_tweet(text)&lt;br /&gt;
        raise NotImplementedError(&amp;quot;You need to implement this to reply to/fav timeline tweets (or pass if you don&#039;t want to)!&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def on_follow(self, f_id):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Perform some action when followed.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        if self.config[&#039;autofollow&#039;]:&lt;br /&gt;
            try:&lt;br /&gt;
                self.api.create_friendship(f_id, follow=True)&lt;br /&gt;
                self.state[&#039;friends&#039;].append(f_id)&lt;br /&gt;
                logging.info(&#039;Followed user id {}&#039;.format(f_id))&lt;br /&gt;
            except tweepy.TweepError as e:&lt;br /&gt;
                self._log_tweepy_error(&#039;Unable to follow user&#039;, e)&lt;br /&gt;
&lt;br /&gt;
            time.sleep(3)&lt;br /&gt;
&lt;br /&gt;
        self.state[&#039;followers&#039;].append(f_id)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def post_tweet(self, text, reply_to=None, media=None, file=None):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Post a tweet containing the text. &lt;br /&gt;
        If you provide a filename the file will be added to the tweet.&lt;br /&gt;
        You can also provide the filedata, for example when using dynamically created images.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
        kwargs = { &amp;quot;status&amp;quot;: text }&lt;br /&gt;
        args = []&lt;br /&gt;
&lt;br /&gt;
        if media is not None:&lt;br /&gt;
            cmd = self.api.update_with_media&lt;br /&gt;
            args.insert(0, media)&lt;br /&gt;
&lt;br /&gt;
            if file is not None:&lt;br /&gt;
                kwargs[&amp;quot;file&amp;quot;] = file&lt;br /&gt;
        else:&lt;br /&gt;
            cmd = self.api.update_status&lt;br /&gt;
&lt;br /&gt;
        try:&lt;br /&gt;
            self.log(&#039;Tweeting &amp;quot;{}&amp;quot;&#039;.format(text))&lt;br /&gt;
            if reply_to:&lt;br /&gt;
                self.log(&amp;quot;-- Responding to status {}&amp;quot;.format(self._tweet_url(reply_to)))&lt;br /&gt;
                kwargs[&#039;in_reply_to_status_id&#039;] = reply_to.id&lt;br /&gt;
            else:&lt;br /&gt;
                self.log(&amp;quot;-- Posting to own timeline&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
            tweet = cmd(*args, **kwargs)&lt;br /&gt;
            self.log(&#039;Status posted at {}&#039;.format(self._tweet_url(tweet)))&lt;br /&gt;
            return True&lt;br /&gt;
&lt;br /&gt;
        except tweepy.TweepError as e:&lt;br /&gt;
            self._log_tweepy_error(&#039;Can\&#039;t post status&#039;, e)&lt;br /&gt;
            return False&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def favorite_tweet(self, tweet):&lt;br /&gt;
        try:&lt;br /&gt;
            logging.info(&#039;Faving &#039; + self._tweet_url(tweet))&lt;br /&gt;
            self.api.create_favorite(tweet.id)&lt;br /&gt;
&lt;br /&gt;
        except tweepy.TweepError as e:&lt;br /&gt;
            self._log_tweepy_error(&#039;Can\&#039;t fav status&#039;, e)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _ignore_method(self, method):&lt;br /&gt;
        return hasattr(method, &#039;not_implemented&#039;) and method.not_implemented&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _handle_timeline(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Reads the latest tweets in the bots timeline and perform some action.&lt;br /&gt;
        self.recent_timeline&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        for tweet in self.state[&#039;recent_timeline&#039;]:&lt;br /&gt;
            prefix = self.get_mention_prefix(tweet)&lt;br /&gt;
            self.on_timeline(tweet, prefix)&lt;br /&gt;
&lt;br /&gt;
            words = tweet.text.lower().split()&lt;br /&gt;
            if any(w in words for w in self.config[&#039;autofav_keywords&#039;]):&lt;br /&gt;
                self.favorite_tweet(tweet)&lt;br /&gt;
&lt;br /&gt;
            #time.sleep(self.config[&#039;reply_interval&#039;])&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _handle_mentions(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Performs some action on the mentions in self.mention_queue&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        # TODO: only handle a certain number of mentions at a time?&lt;br /&gt;
        for mention in iter(self.state[&#039;mention_queue&#039;]):&lt;br /&gt;
            prefix = self.get_mention_prefix(mention)&lt;br /&gt;
            self.on_mention(mention, prefix)&lt;br /&gt;
            self.state[&#039;mention_queue&#039;].remove(mention)&lt;br /&gt;
&lt;br /&gt;
            if self.config[&#039;autofav_mentions&#039;]:&lt;br /&gt;
                self.favorite_tweet(mention)&lt;br /&gt;
&lt;br /&gt;
            #time.sleep(self.config[&#039;reply_interval&#039;])&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def get_mention_prefix(self, tweet):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Returns a string of users to @-mention when responding to a tweet.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        mention_back = [&#039;@&#039; + tweet.author.screen_name]&lt;br /&gt;
        mention_back += [s for s in re.split(&#039;[^@\w]&#039;, tweet.text) if len(s) &amp;gt; 2 and s[0] == &#039;@&#039; and s[1:] != self.screen_name]&lt;br /&gt;
&lt;br /&gt;
        if self.config[&#039;reply_followers_only&#039;]:&lt;br /&gt;
            mention_back = [s for s in mention_back if s[1:] in self.state[&#039;followers&#039;] or s == &#039;@&#039; + tweet.author.screen_name]&lt;br /&gt;
&lt;br /&gt;
        return &#039; &#039;.join(mention_back)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _check_mentions(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Checks mentions and loads most recent tweets into the mention queue&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        if self._ignore_method(self.on_mention):&lt;br /&gt;
            logging.debug(&#039;Ignoring mentions&#039;)&lt;br /&gt;
            return&lt;br /&gt;
&lt;br /&gt;
        try:&lt;br /&gt;
            current_mentions = self.api.mentions_timeline(since_id=self.state[&#039;last_mention_id&#039;], count=100)&lt;br /&gt;
&lt;br /&gt;
            # direct mentions only?&lt;br /&gt;
            if self.config[&#039;reply_direct_mention_only&#039;]:&lt;br /&gt;
                current_mentions = [t for t in current_mentions if re.split(&#039;[^@\w]&#039;, t.text)[0] == &#039;@&#039; + self.screen_name]&lt;br /&gt;
&lt;br /&gt;
            if len(current_mentions) != 0:&lt;br /&gt;
                self.state[&#039;last_mention_id&#039;] = current_mentions[0].id&lt;br /&gt;
            &lt;br /&gt;
            self.state[&#039;last_mention_time&#039;] = time.time()&lt;br /&gt;
&lt;br /&gt;
            self.state[&#039;mention_queue&#039;] += reversed(current_mentions)&lt;br /&gt;
&lt;br /&gt;
            logging.info(&#039;Mentions updated ({} retrieved, {} total in queue)&#039;.format(len(current_mentions), len(self.state[&#039;mention_queue&#039;])))&lt;br /&gt;
&lt;br /&gt;
        except tweepy.TweepError as e:&lt;br /&gt;
            self._log_tweepy_error(&#039;Can\&#039;t retrieve mentions&#039;, e)&lt;br /&gt;
&lt;br /&gt;
        except IncompleteRead as e:&lt;br /&gt;
            self.log(&#039;Incomplete read error -- skipping mentions update&#039;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _check_timeline(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Checks timeline and loads most recent tweets into recent timeline&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        if self._ignore_method(self.on_timeline):&lt;br /&gt;
            logging.debug(&#039;Ignoring timeline&#039;)&lt;br /&gt;
            return&lt;br /&gt;
&lt;br /&gt;
        try:&lt;br /&gt;
            current_timeline = self.api.home_timeline(count=200, since_id=self.state[&#039;last_timeline_id&#039;])&lt;br /&gt;
&lt;br /&gt;
            # remove my tweets&lt;br /&gt;
            current_timeline = [t for t in current_timeline if t.author.screen_name.lower() != self.screen_name.lower()]&lt;br /&gt;
&lt;br /&gt;
            # remove all tweets mentioning me&lt;br /&gt;
            current_timeline = [t for t in current_timeline if not re.search(&#039;@&#039;+self.screen_name, t.text, flags=re.IGNORECASE)]&lt;br /&gt;
&lt;br /&gt;
            if self.config[&#039;ignore_timeline_mentions&#039;]:&lt;br /&gt;
                # remove all tweets with mentions (heuristically)&lt;br /&gt;
                current_timeline = [t for t in current_timeline if &#039;@&#039; not in t.text]&lt;br /&gt;
&lt;br /&gt;
            if len(current_timeline) != 0:&lt;br /&gt;
                self.state[&#039;last_timeline_id&#039;] = current_timeline[0].id&lt;br /&gt;
            &lt;br /&gt;
            self.state[&#039;last_timeline_time&#039;] = time.time()&lt;br /&gt;
&lt;br /&gt;
            self.state[&#039;recent_timeline&#039;] = list(reversed(current_timeline))&lt;br /&gt;
&lt;br /&gt;
            logging.info(&#039;Timeline updated ({} retrieved)&#039;.format(len(current_timeline)))&lt;br /&gt;
&lt;br /&gt;
        except tweepy.TweepError as e:&lt;br /&gt;
            self._log_tweepy_error(&#039;Can\&#039;t retrieve timeline&#039;, e)&lt;br /&gt;
&lt;br /&gt;
        except IncompleteRead as e:&lt;br /&gt;
            self.log(&#039;Incomplete read error -- skipping timeline update&#039;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _check_followers(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Checks followers.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        logging.info(&amp;quot;Checking for new followers...&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
        try:&lt;br /&gt;
            self.state[&#039;new_followers&#039;] = [f_id for f_id in self.api.followers_ids(self.id) if f_id not in self.state[&#039;followers&#039;]]&lt;br /&gt;
&lt;br /&gt;
            self.config[&#039;last_follow_check&#039;] = time.time()&lt;br /&gt;
&lt;br /&gt;
        except tweepy.TweepError as e:&lt;br /&gt;
            self._log_tweepy_error(&#039;Can\&#039;t update followers&#039;, e)&lt;br /&gt;
&lt;br /&gt;
        except IncompleteRead as e:&lt;br /&gt;
            self.log(&#039;Incomplete read error -- skipping followers update&#039;)&lt;br /&gt;
&lt;br /&gt;
            &lt;br /&gt;
    def _handle_followers(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Handles new followers.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        for f_id in self.state[&#039;new_followers&#039;]:&lt;br /&gt;
            self.on_follow(f_id)&lt;br /&gt;
&lt;br /&gt;
    def register_custom_handler(self, action, interval):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Register a custom action to run at some interval.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        handler = {}&lt;br /&gt;
&lt;br /&gt;
        handler[&#039;action&#039;] = action&lt;br /&gt;
        handler[&#039;interval&#039;] = interval&lt;br /&gt;
        handler[&#039;last_run&#039;] = 0&lt;br /&gt;
&lt;br /&gt;
        self.custom_handlers.append(handler)&lt;br /&gt;
&lt;br /&gt;
    def run(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Runs the bot! This probably shouldn&#039;t be in a &amp;quot;while True&amp;quot; lol.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        while True:&lt;br /&gt;
            &lt;br /&gt;
            # check followers every 15 minutes&lt;br /&gt;
            #if self.autofollow and (time.time() - self.last_follow_check) &amp;gt; (15 * 60): &lt;br /&gt;
            if self.state[&#039;last_follow_check&#039;] &amp;gt; (15 * 60): &lt;br /&gt;
                self._check_followers()&lt;br /&gt;
                self._handle_followers()&lt;br /&gt;
&lt;br /&gt;
            # check mentions every minute-ish&lt;br /&gt;
            #if self.reply_to_mentions and (time.time() - self.last_mention_time) &amp;gt; 60:&lt;br /&gt;
            if (time.time() - self.state[&#039;last_mention_time&#039;]) &amp;gt; 60:&lt;br /&gt;
                self._check_mentions()&lt;br /&gt;
                self._handle_mentions()&lt;br /&gt;
&lt;br /&gt;
            # tweet to timeline&lt;br /&gt;
            #if self.reply_to_timeline and (time.time() - self.last_mention_time) &amp;gt; 60:&lt;br /&gt;
            if (time.time() - self.state[&#039;last_timeline_time&#039;]) &amp;gt; 60:&lt;br /&gt;
                self._check_timeline()&lt;br /&gt;
                self._handle_timeline()&lt;br /&gt;
&lt;br /&gt;
            # tweet to timeline on the correct interval&lt;br /&gt;
            if (time.time() - self.state[&#039;last_tweet_time&#039;]) &amp;gt; self.config[&#039;tweet_interval&#039;]:&lt;br /&gt;
                self.on_scheduled_tweet()&lt;br /&gt;
&lt;br /&gt;
                # TODO: maybe this should only run if the above is successful...&lt;br /&gt;
                if self.config[&#039;tweet_interval_range&#039;] is not None:&lt;br /&gt;
                    self.config[&#039;tweet_interval&#039;] = random.randint(*self.config[&#039;tweet_interval_range&#039;])&lt;br /&gt;
&lt;br /&gt;
                self.log(&amp;quot;Next tweet in {} seconds&amp;quot;.format(self.config[&#039;tweet_interval&#039;]))&lt;br /&gt;
                self.state[&#039;last_tweet_time&#039;] = time.time()&lt;br /&gt;
&lt;br /&gt;
            # run custom action&lt;br /&gt;
            for handler in self.custom_handlers:&lt;br /&gt;
                if (time.time() - handler[&#039;last_run&#039;]) &amp;gt; handler[&#039;interval&#039;]:&lt;br /&gt;
                    handler[&#039;action&#039;]()&lt;br /&gt;
                    handler[&#039;last_run&#039;] = time.time()&lt;br /&gt;
&lt;br /&gt;
            # save current state&lt;br /&gt;
            self._save_state()&lt;br /&gt;
&lt;br /&gt;
            logging.info(&amp;quot;Sleeping for a bit...&amp;quot;)&lt;br /&gt;
            time.sleep(30)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
class FileStorage(object):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Default storage adapter.&lt;br /&gt;
&lt;br /&gt;
    Adapters must implement two methods: read(name) and write(name).&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def read(self, name):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Return an IO-like object that will produce binary data when read from.&lt;br /&gt;
        If nothing is stored under the given name, raise IOError.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        filename = self._get_filename(name)&lt;br /&gt;
        if os.path.exists(filename):&lt;br /&gt;
            logging.debug(&amp;quot;Reading from {}&amp;quot;.format(filename))&lt;br /&gt;
        else:&lt;br /&gt;
            logging.debug(&amp;quot;{} doesn&#039;t exist&amp;quot;.format(filename))&lt;br /&gt;
        return open(filename, &#039;rb&#039;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def write(self, name):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Return an IO-like object that will store binary data written to it.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        filename = self._get_filename(name)&lt;br /&gt;
        if os.path.exists(filename):&lt;br /&gt;
            logging.debug(&amp;quot;Overwriting {}&amp;quot;.format(filename))&lt;br /&gt;
        else:&lt;br /&gt;
            logging.debug(&amp;quot;Creating {}&amp;quot;.format(filename))&lt;br /&gt;
        return open(filename, &#039;wb&#039;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _get_filename(self, name):&lt;br /&gt;
        return &#039;{}_state.pkl&#039;.format(name)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>SebastianStang</name></author>
	</entry>
	<entry>
		<id>https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=GMU:Bots_%27n%27_Plots/Sebastian_Stang&amp;diff=72829</id>
		<title>GMU:Bots &#039;n&#039; Plots/Sebastian Stang</title>
		<link rel="alternate" type="text/html" href="https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=GMU:Bots_%27n%27_Plots/Sebastian_Stang&amp;diff=72829"/>
		<updated>2015-06-17T16:04:39Z</updated>

		<summary type="html">&lt;p&gt;SebastianStang: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;( )   ( )&amp;lt;br/&amp;gt;&lt;br /&gt;
(&amp;gt;•.•&amp;lt;)&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;nowiki&amp;gt;  (&amp;quot;)    (&amp;quot;)&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== @_pil_bot_ ==&lt;br /&gt;
&lt;br /&gt;
[[File:Pilbot1.png|480px|thumb|left]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Pilbot2.png|480px|thumb|left]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr  style=&amp;quot;clear:both&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== pilbot.py ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
# -*- coding: utf-8 -*- #&lt;br /&gt;
&lt;br /&gt;
from twitterbot import TwitterBot&lt;br /&gt;
import keys&lt;br /&gt;
&lt;br /&gt;
from PIL import Image&lt;br /&gt;
from sys import argv&lt;br /&gt;
from random import random&lt;br /&gt;
&lt;br /&gt;
from pileval import PILEval&lt;br /&gt;
&lt;br /&gt;
class PilBot(TwitterBot):&lt;br /&gt;
    &lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        TwitterBot.__init__(self)&lt;br /&gt;
        self.pillermann = PILEval()&lt;br /&gt;
        &lt;br /&gt;
    def bot_init(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Initialize and configure the bot &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
        ############################&lt;br /&gt;
        # REQUIRED: LOGIN DETAILS! #&lt;br /&gt;
        ############################&lt;br /&gt;
        self.config[&#039;api_key&#039;] = keys.consumer_key  &lt;br /&gt;
        self.config[&#039;api_secret&#039;] = keys.consumer_secret&lt;br /&gt;
        self.config[&#039;access_key&#039;] = keys.access_token&lt;br /&gt;
        self.config[&#039;access_secret&#039;] = keys.access_token_secret&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        ######################################&lt;br /&gt;
        # SEMI-OPTIONAL: OTHER CONFIG STUFF! #&lt;br /&gt;
        ######################################&lt;br /&gt;
&lt;br /&gt;
        # how often to tweet, in seconds&lt;br /&gt;
        self.config[&#039;tweet_interval&#039;] = 1 * 5     # default: 1 minutes&lt;br /&gt;
&lt;br /&gt;
        # use this to define a (min, max) random range of how often to tweet&lt;br /&gt;
        # e.g., self.config[&#039;tweet_interval_range&#039;] = (5*60, 10*60) # tweets every 5-10 minutes&lt;br /&gt;
        self.config[&#039;tweet_interval_range&#039;] = None&lt;br /&gt;
&lt;br /&gt;
        # only reply to tweets that specifically mention the bot&lt;br /&gt;
        self.config[&#039;reply_direct_mention_only&#039;] = True&lt;br /&gt;
&lt;br /&gt;
        # only include bot followers (and original tweeter) in @-replies&lt;br /&gt;
        self.config[&#039;reply_followers_only&#039;] = False&lt;br /&gt;
&lt;br /&gt;
        # fav any tweets that mention this bot?&lt;br /&gt;
        self.config[&#039;autofav_mentions&#039;] = False&lt;br /&gt;
&lt;br /&gt;
        # fav any tweets containing these keywords?&lt;br /&gt;
        self.config[&#039;autofav_keywords&#039;] = []&lt;br /&gt;
&lt;br /&gt;
        # follow back all followers?&lt;br /&gt;
        self.config[&#039;autofollow&#039;] = False&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def on_scheduled_tweet(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Make a public tweet to the bot&#039;s own timeline. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        # We might take senteces from somewhere and tweet them on a regular basis ...&lt;br /&gt;
        pass # don&#039;t do anything here ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def on_mention(self, tweet, prefix):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Actions to take when a mention is received. &amp;quot;&amp;quot;&amp;quot;     &lt;br /&gt;
        text = tweet.text.split()[1]&lt;br /&gt;
        self.action(text)&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
    def on_timeline(self, tweet, prefix):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Actions to take on a timeline tweet. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        pass&lt;br /&gt;
    &lt;br /&gt;
    def action(self, code):&lt;br /&gt;
        result = self.pillermann.eval(code)&lt;br /&gt;
        &lt;br /&gt;
        if result: # post the image&lt;br /&gt;
            self.post_tweet(code, media=&amp;quot;img.png&amp;quot;, file=self.pillermann.get_file())        &lt;br /&gt;
        else: # post the error&lt;br /&gt;
            self.post_tweet(self.pillermann.last_error)        &lt;br /&gt;
    &lt;br /&gt;
if __name__ == &#039;__main__&#039;:&lt;br /&gt;
&lt;br /&gt;
    bot = PilBot()&lt;br /&gt;
    &lt;br /&gt;
    if len(argv) == 1:&lt;br /&gt;
        bot.run()&lt;br /&gt;
    else:     &lt;br /&gt;
        tweet_text = &amp;quot;text((300, 400), &#039;botsNplots&#039;, fill=&#039;#ff0&#039;)&amp;quot;        &lt;br /&gt;
        bot.action(tweet_text)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== pileval.py ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
from PIL import Image, ImageDraw&lt;br /&gt;
&lt;br /&gt;
from os import path&lt;br /&gt;
from io import BytesIO&lt;br /&gt;
&lt;br /&gt;
class PILEval():&lt;br /&gt;
    &lt;br /&gt;
    def __init__(self, filename=&#039;test.png&#039;):&lt;br /&gt;
        self.filename = filename&lt;br /&gt;
        try:&lt;br /&gt;
            self.image = Image.open(filename)&lt;br /&gt;
        except FileNotFoundError as e:&lt;br /&gt;
            self.image = Image.new(&amp;quot;RGBA&amp;quot;, (640, 480), &#039;white&#039;)        &lt;br /&gt;
        self.last_error = &#039;&#039;&lt;br /&gt;
        &lt;br /&gt;
    def __repr__(self):&lt;br /&gt;
        return &amp;quot;&amp;lt;PILEval: %s&amp;gt;&amp;quot; % self.filename&lt;br /&gt;
            &lt;br /&gt;
    def eval(self, code):&lt;br /&gt;
        draw = ImageDraw.Draw(self.image, &amp;quot;RGBA&amp;quot;)        &lt;br /&gt;
        code = &amp;quot;draw.%s&amp;quot; % code&lt;br /&gt;
&lt;br /&gt;
        try:&lt;br /&gt;
            code_obj = compile(code, &#039;&amp;lt;string&amp;gt;&#039;, &#039;exec&#039;)&lt;br /&gt;
            exec(code_obj)&lt;br /&gt;
        except Exception as e:&lt;br /&gt;
            self.last_error = str(e)&lt;br /&gt;
            return False            &lt;br /&gt;
        &lt;br /&gt;
        self.image.save(self.filename)&lt;br /&gt;
   &lt;br /&gt;
        return True&lt;br /&gt;
    &lt;br /&gt;
    def get_file(self):&lt;br /&gt;
        img = Image.open(self.filename)&lt;br /&gt;
        file = BytesIO()&lt;br /&gt;
        img.save(file, format=&#039;PNG&#039;)&lt;br /&gt;
        return file&lt;br /&gt;
        &lt;br /&gt;
if __name__ == &#039;__main__&#039;:&lt;br /&gt;
    # some q&#039;n&#039;dirty tests&lt;br /&gt;
    print(&amp;quot;main&amp;quot;)&lt;br /&gt;
    pillermann = PILEval()&lt;br /&gt;
&lt;br /&gt;
    # will work&lt;br /&gt;
    # result = pillermann.eval(&amp;quot;rectangle([20, 10, 120, 200], fill=&#039;#f00&#039;)&amp;quot;)&lt;br /&gt;
    # if result:&lt;br /&gt;
    #     print(&#039;good&#039;)&lt;br /&gt;
        &lt;br /&gt;
    # will also work&lt;br /&gt;
    # result = pillermann.eval(&amp;quot;ellipse([200, 50, 500, 500], fill=&#039;#def&#039;)&amp;quot;)&lt;br /&gt;
    # result = pillermann.eval(&amp;quot;point((123, 234), fill=&#039;#345&#039;)&amp;quot;)&lt;br /&gt;
    # result = pillermann.eval(&amp;quot;line([23, 34, 345, 456], fill=&#039;#eca&#039;, width=10)&amp;quot;)&lt;br /&gt;
    # result = pillermann.eval(&amp;quot;arc([500, 10, 600, 440], 10, 95, fill=&#039;#3f9&#039;)&amp;quot;)&lt;br /&gt;
    # result = pillermann.eval(&amp;quot;text((22, 202), &#039;_pil_bot_!&#039;, fill=&#039;#a4f&#039;)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
    result = pillermann.eval(&amp;quot;text((100, 100), text=code, fill=&#039;#000&#039;)&amp;quot;)&lt;br /&gt;
    if result:&lt;br /&gt;
        print(&amp;quot;ok&amp;quot;)&lt;br /&gt;
    else:&lt;br /&gt;
        print(pillermann.last_error)&lt;br /&gt;
    # will yield a compiler error&lt;br /&gt;
    # result = pillermann.eval(&amp;quot;rectangle([20, 10, 120, 200], fill=&#039;#f00&#039;&amp;quot;)&lt;br /&gt;
    # if not result:&lt;br /&gt;
    #     print(&amp;quot;error result: %s&amp;quot; % pillermann.last_error)      &lt;br /&gt;
&lt;br /&gt;
    # f = pillermann.get_file()&lt;br /&gt;
    # print(f)&lt;br /&gt;
    &lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== twitterbot/bot.py === &lt;br /&gt;
&lt;br /&gt;
adapts thricedotted&#039;s (http://lczzz.me) twitterbot for python 3&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
import os&lt;br /&gt;
import codecs&lt;br /&gt;
import json&lt;br /&gt;
import logging&lt;br /&gt;
import tweepy&lt;br /&gt;
import time&lt;br /&gt;
import re&lt;br /&gt;
import random&lt;br /&gt;
import pickle&lt;br /&gt;
&lt;br /&gt;
def ignore(method):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Use the @ignore decorator on TwitterBot methods you wish to leave&lt;br /&gt;
    unimplemented, such as on_timeline and on_mention.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    method.not_implemented = True&lt;br /&gt;
    return method&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
class TwitterBot:&lt;br /&gt;
&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        self.config = {}&lt;br /&gt;
&lt;br /&gt;
        self.custom_handlers = []&lt;br /&gt;
&lt;br /&gt;
        self.config[&#039;reply_direct_mention_only&#039;] = False&lt;br /&gt;
        self.config[&#039;reply_followers_only&#039;] = True&lt;br /&gt;
&lt;br /&gt;
        self.config[&#039;autofav_mentions&#039;] = False&lt;br /&gt;
        self.config[&#039;autofav_keywords&#039;] = []&lt;br /&gt;
&lt;br /&gt;
        self.config[&#039;autofollow&#039;] = False&lt;br /&gt;
&lt;br /&gt;
        self.config[&#039;tweet_interval&#039;] = 30 * 60&lt;br /&gt;
        self.config[&#039;tweet_interval_range&#039;] = None&lt;br /&gt;
&lt;br /&gt;
        self.config[&#039;reply_interval&#039;] = 10&lt;br /&gt;
        self.config[&#039;reply_interval_range&#039;] = None&lt;br /&gt;
&lt;br /&gt;
        self.config[&#039;ignore_timeline_mentions&#039;] = True&lt;br /&gt;
&lt;br /&gt;
        self.config[&#039;logging_level&#039;] = logging.DEBUG&lt;br /&gt;
        self.config[&#039;storage&#039;] = FileStorage()&lt;br /&gt;
&lt;br /&gt;
        self.state = {}&lt;br /&gt;
&lt;br /&gt;
        # call the custom initialization&lt;br /&gt;
        self.bot_init()&lt;br /&gt;
&lt;br /&gt;
        auth = tweepy.OAuthHandler(self.config[&#039;api_key&#039;], self.config[&#039;api_secret&#039;])&lt;br /&gt;
        auth.set_access_token(self.config[&#039;access_key&#039;], self.config[&#039;access_secret&#039;])&lt;br /&gt;
        self.api = tweepy.API(auth)&lt;br /&gt;
&lt;br /&gt;
        self.id = self.api.me().id&lt;br /&gt;
        self.screen_name = self.api.me().screen_name&lt;br /&gt;
&lt;br /&gt;
        logging.basicConfig(format=&#039;%(asctime)s | %(levelname)s: %(message)s&#039;, datefmt=&#039;%m/%d/%Y %I:%M:%S %p&#039;, &lt;br /&gt;
            filename=self.screen_name + &#039;.log&#039;,&lt;br /&gt;
            level=self.config[&#039;logging_level&#039;])&lt;br /&gt;
&lt;br /&gt;
        logging.info(&#039;Initializing bot...&#039;)&lt;br /&gt;
&lt;br /&gt;
        try:&lt;br /&gt;
            with self.config[&#039;storage&#039;].read(self.screen_name) as f:                &lt;br /&gt;
                self.state = pickle.load(f)&lt;br /&gt;
                &lt;br /&gt;
        except IOError:&lt;br /&gt;
            self.state[&#039;last_timeline_id&#039;] = 1&lt;br /&gt;
            self.state[&#039;last_mention_id&#039;] = 1&lt;br /&gt;
&lt;br /&gt;
            self.state[&#039;last_timeline_time&#039;] = 0&lt;br /&gt;
            self.state[&#039;last_mention_time&#039;] = 0&lt;br /&gt;
&lt;br /&gt;
            self.state[&#039;last_tweet_id&#039;] = 1&lt;br /&gt;
            self.state[&#039;last_tweet_time&#039;] = 1&lt;br /&gt;
&lt;br /&gt;
            self.state[&#039;last_reply_id&#039;] = 0&lt;br /&gt;
            self.state[&#039;last_reply_time&#039;] = 0&lt;br /&gt;
&lt;br /&gt;
            self.state[&#039;recent_timeline&#039;] = []&lt;br /&gt;
            self.state[&#039;mention_queue&#039;] = []&lt;br /&gt;
&lt;br /&gt;
        self.state[&#039;friends&#039;] = self.api.friends_ids(self.id)&lt;br /&gt;
        self.state[&#039;followers&#039;] = self.api.followers_ids(self.id)&lt;br /&gt;
        self.state[&#039;new_followers&#039;] = []&lt;br /&gt;
        self.state[&#039;last_follow_check&#039;] = 0&lt;br /&gt;
&lt;br /&gt;
        logging.info(&#039;Bot initialized!&#039;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def bot_init(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Initialize custom state values for your bot.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        raise NotImplementedError(&amp;quot;You MUST have bot_init() implemented in your bot! What have you DONE!&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def log(self, message, level=logging.INFO):&lt;br /&gt;
        if level == logging.ERROR:&lt;br /&gt;
            logging.error(message)&lt;br /&gt;
        else:&lt;br /&gt;
            logging.info(message)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _log_tweepy_error(self, message, e):&lt;br /&gt;
        try:&lt;br /&gt;
            e_message = e.message[0][&#039;message&#039;]&lt;br /&gt;
            code = e.message[0][&#039;code&#039;]&lt;br /&gt;
            self.log(&amp;quot;{}: {} ({})&amp;quot;.format(message, e_message, code), level=logging.ERROR)&lt;br /&gt;
        except:&lt;br /&gt;
            self.log(message, e)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _tweet_url(self, tweet):&lt;br /&gt;
        return &amp;quot;http://twitter.com/&amp;quot; + tweet.author.screen_name + &amp;quot;/status/&amp;quot; + str(tweet.id)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _save_state(self):&lt;br /&gt;
        with self.config[&#039;storage&#039;].write(self.screen_name) as f:&lt;br /&gt;
            pickle.dump(self.state, f)&lt;br /&gt;
            self.log(&#039;Bot state saved&#039;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def on_scheduled_tweet(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Post a general tweet to own timeline.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        #self.post_tweet(text)&lt;br /&gt;
        raise NotImplementedError(&amp;quot;You need to implement this to tweet to timeline (or pass if you don&#039;t want to)!&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def on_mention(self, tweet, prefix):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Perform some action upon receiving a mention.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        #self.post_tweet(text)&lt;br /&gt;
        raise NotImplementedError(&amp;quot;You need to implement this to reply to/fav mentions (or pass if you don&#039;t want to)!&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def on_timeline(self, tweet, prefix):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Perform some action on a tweet on the timeline.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        #self.post_tweet(text)&lt;br /&gt;
        raise NotImplementedError(&amp;quot;You need to implement this to reply to/fav timeline tweets (or pass if you don&#039;t want to)!&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def on_follow(self, f_id):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Perform some action when followed.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        if self.config[&#039;autofollow&#039;]:&lt;br /&gt;
            try:&lt;br /&gt;
                self.api.create_friendship(f_id, follow=True)&lt;br /&gt;
                self.state[&#039;friends&#039;].append(f_id)&lt;br /&gt;
                logging.info(&#039;Followed user id {}&#039;.format(f_id))&lt;br /&gt;
            except tweepy.TweepError as e:&lt;br /&gt;
                self._log_tweepy_error(&#039;Unable to follow user&#039;, e)&lt;br /&gt;
&lt;br /&gt;
            time.sleep(3)&lt;br /&gt;
&lt;br /&gt;
        self.state[&#039;followers&#039;].append(f_id)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def post_tweet(self, text, reply_to=None, media=None, file=None):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Post a tweet containing the text. &lt;br /&gt;
        If you provide a filename the file will be added to the tweet.&lt;br /&gt;
        You can also provide the filedata, for example when using dynamically created images.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
        kwargs = { &amp;quot;status&amp;quot;: text }&lt;br /&gt;
        args = []&lt;br /&gt;
&lt;br /&gt;
        if media is not None:&lt;br /&gt;
            cmd = self.api.update_with_media&lt;br /&gt;
            args.insert(0, media)&lt;br /&gt;
&lt;br /&gt;
            if file is not None:&lt;br /&gt;
                kwargs[&amp;quot;file&amp;quot;] = file&lt;br /&gt;
        else:&lt;br /&gt;
            cmd = self.api.update_status&lt;br /&gt;
&lt;br /&gt;
        try:&lt;br /&gt;
            self.log(&#039;Tweeting &amp;quot;{}&amp;quot;&#039;.format(text))&lt;br /&gt;
            if reply_to:&lt;br /&gt;
                self.log(&amp;quot;-- Responding to status {}&amp;quot;.format(self._tweet_url(reply_to)))&lt;br /&gt;
                kwargs[&#039;in_reply_to_status_id&#039;] = reply_to.id&lt;br /&gt;
            else:&lt;br /&gt;
                self.log(&amp;quot;-- Posting to own timeline&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
            tweet = cmd(*args, **kwargs)&lt;br /&gt;
            self.log(&#039;Status posted at {}&#039;.format(self._tweet_url(tweet)))&lt;br /&gt;
            return True&lt;br /&gt;
&lt;br /&gt;
        except tweepy.TweepError as e:&lt;br /&gt;
            self._log_tweepy_error(&#039;Can\&#039;t post status&#039;, e)&lt;br /&gt;
            return False&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def favorite_tweet(self, tweet):&lt;br /&gt;
        try:&lt;br /&gt;
            logging.info(&#039;Faving &#039; + self._tweet_url(tweet))&lt;br /&gt;
            self.api.create_favorite(tweet.id)&lt;br /&gt;
&lt;br /&gt;
        except tweepy.TweepError as e:&lt;br /&gt;
            self._log_tweepy_error(&#039;Can\&#039;t fav status&#039;, e)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _ignore_method(self, method):&lt;br /&gt;
        return hasattr(method, &#039;not_implemented&#039;) and method.not_implemented&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _handle_timeline(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Reads the latest tweets in the bots timeline and perform some action.&lt;br /&gt;
        self.recent_timeline&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        for tweet in self.state[&#039;recent_timeline&#039;]:&lt;br /&gt;
            prefix = self.get_mention_prefix(tweet)&lt;br /&gt;
            self.on_timeline(tweet, prefix)&lt;br /&gt;
&lt;br /&gt;
            words = tweet.text.lower().split()&lt;br /&gt;
            if any(w in words for w in self.config[&#039;autofav_keywords&#039;]):&lt;br /&gt;
                self.favorite_tweet(tweet)&lt;br /&gt;
&lt;br /&gt;
            #time.sleep(self.config[&#039;reply_interval&#039;])&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _handle_mentions(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Performs some action on the mentions in self.mention_queue&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        # TODO: only handle a certain number of mentions at a time?&lt;br /&gt;
        for mention in iter(self.state[&#039;mention_queue&#039;]):&lt;br /&gt;
            prefix = self.get_mention_prefix(mention)&lt;br /&gt;
            self.on_mention(mention, prefix)&lt;br /&gt;
            self.state[&#039;mention_queue&#039;].remove(mention)&lt;br /&gt;
&lt;br /&gt;
            if self.config[&#039;autofav_mentions&#039;]:&lt;br /&gt;
                self.favorite_tweet(mention)&lt;br /&gt;
&lt;br /&gt;
            #time.sleep(self.config[&#039;reply_interval&#039;])&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def get_mention_prefix(self, tweet):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Returns a string of users to @-mention when responding to a tweet.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        mention_back = [&#039;@&#039; + tweet.author.screen_name]&lt;br /&gt;
        mention_back += [s for s in re.split(&#039;[^@\w]&#039;, tweet.text) if len(s) &amp;gt; 2 and s[0] == &#039;@&#039; and s[1:] != self.screen_name]&lt;br /&gt;
&lt;br /&gt;
        if self.config[&#039;reply_followers_only&#039;]:&lt;br /&gt;
            mention_back = [s for s in mention_back if s[1:] in self.state[&#039;followers&#039;] or s == &#039;@&#039; + tweet.author.screen_name]&lt;br /&gt;
&lt;br /&gt;
        return &#039; &#039;.join(mention_back)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _check_mentions(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Checks mentions and loads most recent tweets into the mention queue&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        if self._ignore_method(self.on_mention):&lt;br /&gt;
            logging.debug(&#039;Ignoring mentions&#039;)&lt;br /&gt;
            return&lt;br /&gt;
&lt;br /&gt;
        try:&lt;br /&gt;
            current_mentions = self.api.mentions_timeline(since_id=self.state[&#039;last_mention_id&#039;], count=100)&lt;br /&gt;
&lt;br /&gt;
            # direct mentions only?&lt;br /&gt;
            if self.config[&#039;reply_direct_mention_only&#039;]:&lt;br /&gt;
                current_mentions = [t for t in current_mentions if re.split(&#039;[^@\w]&#039;, t.text)[0] == &#039;@&#039; + self.screen_name]&lt;br /&gt;
&lt;br /&gt;
            if len(current_mentions) != 0:&lt;br /&gt;
                self.state[&#039;last_mention_id&#039;] = current_mentions[0].id&lt;br /&gt;
            &lt;br /&gt;
            self.state[&#039;last_mention_time&#039;] = time.time()&lt;br /&gt;
&lt;br /&gt;
            self.state[&#039;mention_queue&#039;] += reversed(current_mentions)&lt;br /&gt;
&lt;br /&gt;
            logging.info(&#039;Mentions updated ({} retrieved, {} total in queue)&#039;.format(len(current_mentions), len(self.state[&#039;mention_queue&#039;])))&lt;br /&gt;
&lt;br /&gt;
        except tweepy.TweepError as e:&lt;br /&gt;
            self._log_tweepy_error(&#039;Can\&#039;t retrieve mentions&#039;, e)&lt;br /&gt;
&lt;br /&gt;
        except IncompleteRead as e:&lt;br /&gt;
            self.log(&#039;Incomplete read error -- skipping mentions update&#039;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _check_timeline(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Checks timeline and loads most recent tweets into recent timeline&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        if self._ignore_method(self.on_timeline):&lt;br /&gt;
            logging.debug(&#039;Ignoring timeline&#039;)&lt;br /&gt;
            return&lt;br /&gt;
&lt;br /&gt;
        try:&lt;br /&gt;
            current_timeline = self.api.home_timeline(count=200, since_id=self.state[&#039;last_timeline_id&#039;])&lt;br /&gt;
&lt;br /&gt;
            # remove my tweets&lt;br /&gt;
            current_timeline = [t for t in current_timeline if t.author.screen_name.lower() != self.screen_name.lower()]&lt;br /&gt;
&lt;br /&gt;
            # remove all tweets mentioning me&lt;br /&gt;
            current_timeline = [t for t in current_timeline if not re.search(&#039;@&#039;+self.screen_name, t.text, flags=re.IGNORECASE)]&lt;br /&gt;
&lt;br /&gt;
            if self.config[&#039;ignore_timeline_mentions&#039;]:&lt;br /&gt;
                # remove all tweets with mentions (heuristically)&lt;br /&gt;
                current_timeline = [t for t in current_timeline if &#039;@&#039; not in t.text]&lt;br /&gt;
&lt;br /&gt;
            if len(current_timeline) != 0:&lt;br /&gt;
                self.state[&#039;last_timeline_id&#039;] = current_timeline[0].id&lt;br /&gt;
            &lt;br /&gt;
            self.state[&#039;last_timeline_time&#039;] = time.time()&lt;br /&gt;
&lt;br /&gt;
            self.state[&#039;recent_timeline&#039;] = list(reversed(current_timeline))&lt;br /&gt;
&lt;br /&gt;
            logging.info(&#039;Timeline updated ({} retrieved)&#039;.format(len(current_timeline)))&lt;br /&gt;
&lt;br /&gt;
        except tweepy.TweepError as e:&lt;br /&gt;
            self._log_tweepy_error(&#039;Can\&#039;t retrieve timeline&#039;, e)&lt;br /&gt;
&lt;br /&gt;
        except IncompleteRead as e:&lt;br /&gt;
            self.log(&#039;Incomplete read error -- skipping timeline update&#039;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _check_followers(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Checks followers.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        logging.info(&amp;quot;Checking for new followers...&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
        try:&lt;br /&gt;
            self.state[&#039;new_followers&#039;] = [f_id for f_id in self.api.followers_ids(self.id) if f_id not in self.state[&#039;followers&#039;]]&lt;br /&gt;
&lt;br /&gt;
            self.config[&#039;last_follow_check&#039;] = time.time()&lt;br /&gt;
&lt;br /&gt;
        except tweepy.TweepError as e:&lt;br /&gt;
            self._log_tweepy_error(&#039;Can\&#039;t update followers&#039;, e)&lt;br /&gt;
&lt;br /&gt;
        except IncompleteRead as e:&lt;br /&gt;
            self.log(&#039;Incomplete read error -- skipping followers update&#039;)&lt;br /&gt;
&lt;br /&gt;
            &lt;br /&gt;
    def _handle_followers(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Handles new followers.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        for f_id in self.state[&#039;new_followers&#039;]:&lt;br /&gt;
            self.on_follow(f_id)&lt;br /&gt;
&lt;br /&gt;
    def register_custom_handler(self, action, interval):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Register a custom action to run at some interval.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        handler = {}&lt;br /&gt;
&lt;br /&gt;
        handler[&#039;action&#039;] = action&lt;br /&gt;
        handler[&#039;interval&#039;] = interval&lt;br /&gt;
        handler[&#039;last_run&#039;] = 0&lt;br /&gt;
&lt;br /&gt;
        self.custom_handlers.append(handler)&lt;br /&gt;
&lt;br /&gt;
    def run(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Runs the bot! This probably shouldn&#039;t be in a &amp;quot;while True&amp;quot; lol.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        while True:&lt;br /&gt;
            &lt;br /&gt;
            # check followers every 15 minutes&lt;br /&gt;
            #if self.autofollow and (time.time() - self.last_follow_check) &amp;gt; (15 * 60): &lt;br /&gt;
            if self.state[&#039;last_follow_check&#039;] &amp;gt; (15 * 60): &lt;br /&gt;
                self._check_followers()&lt;br /&gt;
                self._handle_followers()&lt;br /&gt;
&lt;br /&gt;
            # check mentions every minute-ish&lt;br /&gt;
            #if self.reply_to_mentions and (time.time() - self.last_mention_time) &amp;gt; 60:&lt;br /&gt;
            if (time.time() - self.state[&#039;last_mention_time&#039;]) &amp;gt; 60:&lt;br /&gt;
                self._check_mentions()&lt;br /&gt;
                self._handle_mentions()&lt;br /&gt;
&lt;br /&gt;
            # tweet to timeline&lt;br /&gt;
            #if self.reply_to_timeline and (time.time() - self.last_mention_time) &amp;gt; 60:&lt;br /&gt;
            if (time.time() - self.state[&#039;last_timeline_time&#039;]) &amp;gt; 60:&lt;br /&gt;
                self._check_timeline()&lt;br /&gt;
                self._handle_timeline()&lt;br /&gt;
&lt;br /&gt;
            # tweet to timeline on the correct interval&lt;br /&gt;
            if (time.time() - self.state[&#039;last_tweet_time&#039;]) &amp;gt; self.config[&#039;tweet_interval&#039;]:&lt;br /&gt;
                self.on_scheduled_tweet()&lt;br /&gt;
&lt;br /&gt;
                # TODO: maybe this should only run if the above is successful...&lt;br /&gt;
                if self.config[&#039;tweet_interval_range&#039;] is not None:&lt;br /&gt;
                    self.config[&#039;tweet_interval&#039;] = random.randint(*self.config[&#039;tweet_interval_range&#039;])&lt;br /&gt;
&lt;br /&gt;
                self.log(&amp;quot;Next tweet in {} seconds&amp;quot;.format(self.config[&#039;tweet_interval&#039;]))&lt;br /&gt;
                self.state[&#039;last_tweet_time&#039;] = time.time()&lt;br /&gt;
&lt;br /&gt;
            # run custom action&lt;br /&gt;
            for handler in self.custom_handlers:&lt;br /&gt;
                if (time.time() - handler[&#039;last_run&#039;]) &amp;gt; handler[&#039;interval&#039;]:&lt;br /&gt;
                    handler[&#039;action&#039;]()&lt;br /&gt;
                    handler[&#039;last_run&#039;] = time.time()&lt;br /&gt;
&lt;br /&gt;
            # save current state&lt;br /&gt;
            self._save_state()&lt;br /&gt;
&lt;br /&gt;
            logging.info(&amp;quot;Sleeping for a bit...&amp;quot;)&lt;br /&gt;
            time.sleep(30)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
class FileStorage(object):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Default storage adapter.&lt;br /&gt;
&lt;br /&gt;
    Adapters must implement two methods: read(name) and write(name).&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def read(self, name):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Return an IO-like object that will produce binary data when read from.&lt;br /&gt;
        If nothing is stored under the given name, raise IOError.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        filename = self._get_filename(name)&lt;br /&gt;
        if os.path.exists(filename):&lt;br /&gt;
            logging.debug(&amp;quot;Reading from {}&amp;quot;.format(filename))&lt;br /&gt;
        else:&lt;br /&gt;
            logging.debug(&amp;quot;{} doesn&#039;t exist&amp;quot;.format(filename))&lt;br /&gt;
        return open(filename, &#039;rb&#039;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def write(self, name):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Return an IO-like object that will store binary data written to it.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        filename = self._get_filename(name)&lt;br /&gt;
        if os.path.exists(filename):&lt;br /&gt;
            logging.debug(&amp;quot;Overwriting {}&amp;quot;.format(filename))&lt;br /&gt;
        else:&lt;br /&gt;
            logging.debug(&amp;quot;Creating {}&amp;quot;.format(filename))&lt;br /&gt;
        return open(filename, &#039;wb&#039;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _get_filename(self, name):&lt;br /&gt;
        return &#039;{}_state.pkl&#039;.format(name)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>SebastianStang</name></author>
	</entry>
	<entry>
		<id>https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=File:Pilbot2.png&amp;diff=72828</id>
		<title>File:Pilbot2.png</title>
		<link rel="alternate" type="text/html" href="https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=File:Pilbot2.png&amp;diff=72828"/>
		<updated>2015-06-17T16:01:08Z</updated>

		<summary type="html">&lt;p&gt;SebastianStang: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
&lt;br /&gt;
== Copyright status: ==&lt;br /&gt;
&lt;br /&gt;
== Source: ==&lt;/div&gt;</summary>
		<author><name>SebastianStang</name></author>
	</entry>
	<entry>
		<id>https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=File:Pilbot1.png&amp;diff=72827</id>
		<title>File:Pilbot1.png</title>
		<link rel="alternate" type="text/html" href="https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=File:Pilbot1.png&amp;diff=72827"/>
		<updated>2015-06-17T16:00:47Z</updated>

		<summary type="html">&lt;p&gt;SebastianStang: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
&lt;br /&gt;
== Copyright status: ==&lt;br /&gt;
&lt;br /&gt;
== Source: ==&lt;/div&gt;</summary>
		<author><name>SebastianStang</name></author>
	</entry>
	<entry>
		<id>https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=GMU:Bots_%27n%27_Plots/Sebastian_Stang&amp;diff=72826</id>
		<title>GMU:Bots &#039;n&#039; Plots/Sebastian Stang</title>
		<link rel="alternate" type="text/html" href="https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=GMU:Bots_%27n%27_Plots/Sebastian_Stang&amp;diff=72826"/>
		<updated>2015-06-17T15:58:01Z</updated>

		<summary type="html">&lt;p&gt;SebastianStang: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;( )   ( )&amp;lt;br/&amp;gt;&lt;br /&gt;
(&amp;gt;•.•&amp;lt;)&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;nowiki&amp;gt;  (&amp;quot;)    (&amp;quot;)&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== @_pil_bot_ ==&lt;br /&gt;
&lt;br /&gt;
[[File:pil__bot.png|500px|thumb|left]]&lt;br /&gt;
&lt;br /&gt;
=== pilbot.py ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
# -*- coding: utf-8 -*- #&lt;br /&gt;
&lt;br /&gt;
from twitterbot import TwitterBot&lt;br /&gt;
import keys&lt;br /&gt;
&lt;br /&gt;
from PIL import Image&lt;br /&gt;
from sys import argv&lt;br /&gt;
from random import random&lt;br /&gt;
&lt;br /&gt;
from pileval import PILEval&lt;br /&gt;
&lt;br /&gt;
class PilBot(TwitterBot):&lt;br /&gt;
    &lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        TwitterBot.__init__(self)&lt;br /&gt;
        self.pillermann = PILEval()&lt;br /&gt;
        &lt;br /&gt;
    def bot_init(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Initialize and configure the bot &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
        ############################&lt;br /&gt;
        # REQUIRED: LOGIN DETAILS! #&lt;br /&gt;
        ############################&lt;br /&gt;
        self.config[&#039;api_key&#039;] = keys.consumer_key  &lt;br /&gt;
        self.config[&#039;api_secret&#039;] = keys.consumer_secret&lt;br /&gt;
        self.config[&#039;access_key&#039;] = keys.access_token&lt;br /&gt;
        self.config[&#039;access_secret&#039;] = keys.access_token_secret&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        ######################################&lt;br /&gt;
        # SEMI-OPTIONAL: OTHER CONFIG STUFF! #&lt;br /&gt;
        ######################################&lt;br /&gt;
&lt;br /&gt;
        # how often to tweet, in seconds&lt;br /&gt;
        self.config[&#039;tweet_interval&#039;] = 1 * 5     # default: 1 minutes&lt;br /&gt;
&lt;br /&gt;
        # use this to define a (min, max) random range of how often to tweet&lt;br /&gt;
        # e.g., self.config[&#039;tweet_interval_range&#039;] = (5*60, 10*60) # tweets every 5-10 minutes&lt;br /&gt;
        self.config[&#039;tweet_interval_range&#039;] = None&lt;br /&gt;
&lt;br /&gt;
        # only reply to tweets that specifically mention the bot&lt;br /&gt;
        self.config[&#039;reply_direct_mention_only&#039;] = True&lt;br /&gt;
&lt;br /&gt;
        # only include bot followers (and original tweeter) in @-replies&lt;br /&gt;
        self.config[&#039;reply_followers_only&#039;] = False&lt;br /&gt;
&lt;br /&gt;
        # fav any tweets that mention this bot?&lt;br /&gt;
        self.config[&#039;autofav_mentions&#039;] = False&lt;br /&gt;
&lt;br /&gt;
        # fav any tweets containing these keywords?&lt;br /&gt;
        self.config[&#039;autofav_keywords&#039;] = []&lt;br /&gt;
&lt;br /&gt;
        # follow back all followers?&lt;br /&gt;
        self.config[&#039;autofollow&#039;] = False&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def on_scheduled_tweet(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Make a public tweet to the bot&#039;s own timeline. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        # We might take senteces from somewhere and tweet them on a regular basis ...&lt;br /&gt;
        pass # don&#039;t do anything here ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def on_mention(self, tweet, prefix):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Actions to take when a mention is received. &amp;quot;&amp;quot;&amp;quot;     &lt;br /&gt;
        text = tweet.text.split()[1]&lt;br /&gt;
        self.action(text)&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
    def on_timeline(self, tweet, prefix):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Actions to take on a timeline tweet. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        pass&lt;br /&gt;
    &lt;br /&gt;
    def action(self, code):&lt;br /&gt;
        result = self.pillermann.eval(code)&lt;br /&gt;
        &lt;br /&gt;
        if result: # post the image&lt;br /&gt;
            self.post_tweet(code, media=&amp;quot;img.png&amp;quot;, file=self.pillermann.get_file())        &lt;br /&gt;
        else: # post the error&lt;br /&gt;
            self.post_tweet(self.pillermann.last_error)        &lt;br /&gt;
    &lt;br /&gt;
if __name__ == &#039;__main__&#039;:&lt;br /&gt;
&lt;br /&gt;
    bot = PilBot()&lt;br /&gt;
    &lt;br /&gt;
    if len(argv) == 1:&lt;br /&gt;
        bot.run()&lt;br /&gt;
    else:     &lt;br /&gt;
        tweet_text = &amp;quot;text((300, 400), &#039;botsNplots&#039;, fill=&#039;#ff0&#039;)&amp;quot;        &lt;br /&gt;
        bot.action(tweet_text)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== pileval.py ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
from PIL import Image, ImageDraw&lt;br /&gt;
&lt;br /&gt;
from os import path&lt;br /&gt;
from io import BytesIO&lt;br /&gt;
&lt;br /&gt;
class PILEval():&lt;br /&gt;
    &lt;br /&gt;
    def __init__(self, filename=&#039;test.png&#039;):&lt;br /&gt;
        self.filename = filename&lt;br /&gt;
        try:&lt;br /&gt;
            self.image = Image.open(filename)&lt;br /&gt;
        except FileNotFoundError as e:&lt;br /&gt;
            self.image = Image.new(&amp;quot;RGBA&amp;quot;, (640, 480), &#039;white&#039;)        &lt;br /&gt;
        self.last_error = &#039;&#039;&lt;br /&gt;
        &lt;br /&gt;
    def __repr__(self):&lt;br /&gt;
        return &amp;quot;&amp;lt;PILEval: %s&amp;gt;&amp;quot; % self.filename&lt;br /&gt;
            &lt;br /&gt;
    def eval(self, code):&lt;br /&gt;
        draw = ImageDraw.Draw(self.image, &amp;quot;RGBA&amp;quot;)        &lt;br /&gt;
        code = &amp;quot;draw.%s&amp;quot; % code&lt;br /&gt;
&lt;br /&gt;
        try:&lt;br /&gt;
            code_obj = compile(code, &#039;&amp;lt;string&amp;gt;&#039;, &#039;exec&#039;)&lt;br /&gt;
            exec(code_obj)&lt;br /&gt;
        except Exception as e:&lt;br /&gt;
            self.last_error = str(e)&lt;br /&gt;
            return False            &lt;br /&gt;
        &lt;br /&gt;
        self.image.save(self.filename)&lt;br /&gt;
   &lt;br /&gt;
        return True&lt;br /&gt;
    &lt;br /&gt;
    def get_file(self):&lt;br /&gt;
        img = Image.open(self.filename)&lt;br /&gt;
        file = BytesIO()&lt;br /&gt;
        img.save(file, format=&#039;PNG&#039;)&lt;br /&gt;
        return file&lt;br /&gt;
        &lt;br /&gt;
if __name__ == &#039;__main__&#039;:&lt;br /&gt;
    # some q&#039;n&#039;dirty tests&lt;br /&gt;
    print(&amp;quot;main&amp;quot;)&lt;br /&gt;
    pillermann = PILEval()&lt;br /&gt;
&lt;br /&gt;
    # will work&lt;br /&gt;
    # result = pillermann.eval(&amp;quot;rectangle([20, 10, 120, 200], fill=&#039;#f00&#039;)&amp;quot;)&lt;br /&gt;
    # if result:&lt;br /&gt;
    #     print(&#039;good&#039;)&lt;br /&gt;
        &lt;br /&gt;
    # will also work&lt;br /&gt;
    # result = pillermann.eval(&amp;quot;ellipse([200, 50, 500, 500], fill=&#039;#def&#039;)&amp;quot;)&lt;br /&gt;
    # result = pillermann.eval(&amp;quot;point((123, 234), fill=&#039;#345&#039;)&amp;quot;)&lt;br /&gt;
    # result = pillermann.eval(&amp;quot;line([23, 34, 345, 456], fill=&#039;#eca&#039;, width=10)&amp;quot;)&lt;br /&gt;
    # result = pillermann.eval(&amp;quot;arc([500, 10, 600, 440], 10, 95, fill=&#039;#3f9&#039;)&amp;quot;)&lt;br /&gt;
    # result = pillermann.eval(&amp;quot;text((22, 202), &#039;_pil_bot_!&#039;, fill=&#039;#a4f&#039;)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
    result = pillermann.eval(&amp;quot;text((100, 100), text=code, fill=&#039;#000&#039;)&amp;quot;)&lt;br /&gt;
    if result:&lt;br /&gt;
        print(&amp;quot;ok&amp;quot;)&lt;br /&gt;
    else:&lt;br /&gt;
        print(pillermann.last_error)&lt;br /&gt;
    # will yield a compiler error&lt;br /&gt;
    # result = pillermann.eval(&amp;quot;rectangle([20, 10, 120, 200], fill=&#039;#f00&#039;&amp;quot;)&lt;br /&gt;
    # if not result:&lt;br /&gt;
    #     print(&amp;quot;error result: %s&amp;quot; % pillermann.last_error)      &lt;br /&gt;
&lt;br /&gt;
    # f = pillermann.get_file()&lt;br /&gt;
    # print(f)&lt;br /&gt;
    &lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== twitterbot/bot.py === &lt;br /&gt;
&lt;br /&gt;
adapts thricedotted&#039;s (http://lczzz.me) twitterbot for python 3&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
import os&lt;br /&gt;
import codecs&lt;br /&gt;
import json&lt;br /&gt;
import logging&lt;br /&gt;
import tweepy&lt;br /&gt;
import time&lt;br /&gt;
import re&lt;br /&gt;
import random&lt;br /&gt;
import pickle&lt;br /&gt;
&lt;br /&gt;
def ignore(method):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Use the @ignore decorator on TwitterBot methods you wish to leave&lt;br /&gt;
    unimplemented, such as on_timeline and on_mention.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    method.not_implemented = True&lt;br /&gt;
    return method&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
class TwitterBot:&lt;br /&gt;
&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        self.config = {}&lt;br /&gt;
&lt;br /&gt;
        self.custom_handlers = []&lt;br /&gt;
&lt;br /&gt;
        self.config[&#039;reply_direct_mention_only&#039;] = False&lt;br /&gt;
        self.config[&#039;reply_followers_only&#039;] = True&lt;br /&gt;
&lt;br /&gt;
        self.config[&#039;autofav_mentions&#039;] = False&lt;br /&gt;
        self.config[&#039;autofav_keywords&#039;] = []&lt;br /&gt;
&lt;br /&gt;
        self.config[&#039;autofollow&#039;] = False&lt;br /&gt;
&lt;br /&gt;
        self.config[&#039;tweet_interval&#039;] = 30 * 60&lt;br /&gt;
        self.config[&#039;tweet_interval_range&#039;] = None&lt;br /&gt;
&lt;br /&gt;
        self.config[&#039;reply_interval&#039;] = 10&lt;br /&gt;
        self.config[&#039;reply_interval_range&#039;] = None&lt;br /&gt;
&lt;br /&gt;
        self.config[&#039;ignore_timeline_mentions&#039;] = True&lt;br /&gt;
&lt;br /&gt;
        self.config[&#039;logging_level&#039;] = logging.DEBUG&lt;br /&gt;
        self.config[&#039;storage&#039;] = FileStorage()&lt;br /&gt;
&lt;br /&gt;
        self.state = {}&lt;br /&gt;
&lt;br /&gt;
        # call the custom initialization&lt;br /&gt;
        self.bot_init()&lt;br /&gt;
&lt;br /&gt;
        auth = tweepy.OAuthHandler(self.config[&#039;api_key&#039;], self.config[&#039;api_secret&#039;])&lt;br /&gt;
        auth.set_access_token(self.config[&#039;access_key&#039;], self.config[&#039;access_secret&#039;])&lt;br /&gt;
        self.api = tweepy.API(auth)&lt;br /&gt;
&lt;br /&gt;
        self.id = self.api.me().id&lt;br /&gt;
        self.screen_name = self.api.me().screen_name&lt;br /&gt;
&lt;br /&gt;
        logging.basicConfig(format=&#039;%(asctime)s | %(levelname)s: %(message)s&#039;, datefmt=&#039;%m/%d/%Y %I:%M:%S %p&#039;, &lt;br /&gt;
            filename=self.screen_name + &#039;.log&#039;,&lt;br /&gt;
            level=self.config[&#039;logging_level&#039;])&lt;br /&gt;
&lt;br /&gt;
        logging.info(&#039;Initializing bot...&#039;)&lt;br /&gt;
&lt;br /&gt;
        try:&lt;br /&gt;
            with self.config[&#039;storage&#039;].read(self.screen_name) as f:                &lt;br /&gt;
                self.state = pickle.load(f)&lt;br /&gt;
                &lt;br /&gt;
        except IOError:&lt;br /&gt;
            self.state[&#039;last_timeline_id&#039;] = 1&lt;br /&gt;
            self.state[&#039;last_mention_id&#039;] = 1&lt;br /&gt;
&lt;br /&gt;
            self.state[&#039;last_timeline_time&#039;] = 0&lt;br /&gt;
            self.state[&#039;last_mention_time&#039;] = 0&lt;br /&gt;
&lt;br /&gt;
            self.state[&#039;last_tweet_id&#039;] = 1&lt;br /&gt;
            self.state[&#039;last_tweet_time&#039;] = 1&lt;br /&gt;
&lt;br /&gt;
            self.state[&#039;last_reply_id&#039;] = 0&lt;br /&gt;
            self.state[&#039;last_reply_time&#039;] = 0&lt;br /&gt;
&lt;br /&gt;
            self.state[&#039;recent_timeline&#039;] = []&lt;br /&gt;
            self.state[&#039;mention_queue&#039;] = []&lt;br /&gt;
&lt;br /&gt;
        self.state[&#039;friends&#039;] = self.api.friends_ids(self.id)&lt;br /&gt;
        self.state[&#039;followers&#039;] = self.api.followers_ids(self.id)&lt;br /&gt;
        self.state[&#039;new_followers&#039;] = []&lt;br /&gt;
        self.state[&#039;last_follow_check&#039;] = 0&lt;br /&gt;
&lt;br /&gt;
        logging.info(&#039;Bot initialized!&#039;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def bot_init(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Initialize custom state values for your bot.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        raise NotImplementedError(&amp;quot;You MUST have bot_init() implemented in your bot! What have you DONE!&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def log(self, message, level=logging.INFO):&lt;br /&gt;
        if level == logging.ERROR:&lt;br /&gt;
            logging.error(message)&lt;br /&gt;
        else:&lt;br /&gt;
            logging.info(message)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _log_tweepy_error(self, message, e):&lt;br /&gt;
        try:&lt;br /&gt;
            e_message = e.message[0][&#039;message&#039;]&lt;br /&gt;
            code = e.message[0][&#039;code&#039;]&lt;br /&gt;
            self.log(&amp;quot;{}: {} ({})&amp;quot;.format(message, e_message, code), level=logging.ERROR)&lt;br /&gt;
        except:&lt;br /&gt;
            self.log(message, e)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _tweet_url(self, tweet):&lt;br /&gt;
        return &amp;quot;http://twitter.com/&amp;quot; + tweet.author.screen_name + &amp;quot;/status/&amp;quot; + str(tweet.id)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _save_state(self):&lt;br /&gt;
        with self.config[&#039;storage&#039;].write(self.screen_name) as f:&lt;br /&gt;
            pickle.dump(self.state, f)&lt;br /&gt;
            self.log(&#039;Bot state saved&#039;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def on_scheduled_tweet(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Post a general tweet to own timeline.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        #self.post_tweet(text)&lt;br /&gt;
        raise NotImplementedError(&amp;quot;You need to implement this to tweet to timeline (or pass if you don&#039;t want to)!&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def on_mention(self, tweet, prefix):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Perform some action upon receiving a mention.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        #self.post_tweet(text)&lt;br /&gt;
        raise NotImplementedError(&amp;quot;You need to implement this to reply to/fav mentions (or pass if you don&#039;t want to)!&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def on_timeline(self, tweet, prefix):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Perform some action on a tweet on the timeline.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        #self.post_tweet(text)&lt;br /&gt;
        raise NotImplementedError(&amp;quot;You need to implement this to reply to/fav timeline tweets (or pass if you don&#039;t want to)!&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def on_follow(self, f_id):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Perform some action when followed.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        if self.config[&#039;autofollow&#039;]:&lt;br /&gt;
            try:&lt;br /&gt;
                self.api.create_friendship(f_id, follow=True)&lt;br /&gt;
                self.state[&#039;friends&#039;].append(f_id)&lt;br /&gt;
                logging.info(&#039;Followed user id {}&#039;.format(f_id))&lt;br /&gt;
            except tweepy.TweepError as e:&lt;br /&gt;
                self._log_tweepy_error(&#039;Unable to follow user&#039;, e)&lt;br /&gt;
&lt;br /&gt;
            time.sleep(3)&lt;br /&gt;
&lt;br /&gt;
        self.state[&#039;followers&#039;].append(f_id)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def post_tweet(self, text, reply_to=None, media=None, file=None):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Post a tweet containing the text. &lt;br /&gt;
        If you provide a filename the file will be added to the tweet.&lt;br /&gt;
        You can also provide the filedata, for example when using dynamically created images.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
        kwargs = { &amp;quot;status&amp;quot;: text }&lt;br /&gt;
        args = []&lt;br /&gt;
&lt;br /&gt;
        if media is not None:&lt;br /&gt;
            cmd = self.api.update_with_media&lt;br /&gt;
            args.insert(0, media)&lt;br /&gt;
&lt;br /&gt;
            if file is not None:&lt;br /&gt;
                kwargs[&amp;quot;file&amp;quot;] = file&lt;br /&gt;
        else:&lt;br /&gt;
            cmd = self.api.update_status&lt;br /&gt;
&lt;br /&gt;
        try:&lt;br /&gt;
            self.log(&#039;Tweeting &amp;quot;{}&amp;quot;&#039;.format(text))&lt;br /&gt;
            if reply_to:&lt;br /&gt;
                self.log(&amp;quot;-- Responding to status {}&amp;quot;.format(self._tweet_url(reply_to)))&lt;br /&gt;
                kwargs[&#039;in_reply_to_status_id&#039;] = reply_to.id&lt;br /&gt;
            else:&lt;br /&gt;
                self.log(&amp;quot;-- Posting to own timeline&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
            tweet = cmd(*args, **kwargs)&lt;br /&gt;
            self.log(&#039;Status posted at {}&#039;.format(self._tweet_url(tweet)))&lt;br /&gt;
            return True&lt;br /&gt;
&lt;br /&gt;
        except tweepy.TweepError as e:&lt;br /&gt;
            self._log_tweepy_error(&#039;Can\&#039;t post status&#039;, e)&lt;br /&gt;
            return False&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def favorite_tweet(self, tweet):&lt;br /&gt;
        try:&lt;br /&gt;
            logging.info(&#039;Faving &#039; + self._tweet_url(tweet))&lt;br /&gt;
            self.api.create_favorite(tweet.id)&lt;br /&gt;
&lt;br /&gt;
        except tweepy.TweepError as e:&lt;br /&gt;
            self._log_tweepy_error(&#039;Can\&#039;t fav status&#039;, e)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _ignore_method(self, method):&lt;br /&gt;
        return hasattr(method, &#039;not_implemented&#039;) and method.not_implemented&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _handle_timeline(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Reads the latest tweets in the bots timeline and perform some action.&lt;br /&gt;
        self.recent_timeline&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        for tweet in self.state[&#039;recent_timeline&#039;]:&lt;br /&gt;
            prefix = self.get_mention_prefix(tweet)&lt;br /&gt;
            self.on_timeline(tweet, prefix)&lt;br /&gt;
&lt;br /&gt;
            words = tweet.text.lower().split()&lt;br /&gt;
            if any(w in words for w in self.config[&#039;autofav_keywords&#039;]):&lt;br /&gt;
                self.favorite_tweet(tweet)&lt;br /&gt;
&lt;br /&gt;
            #time.sleep(self.config[&#039;reply_interval&#039;])&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _handle_mentions(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Performs some action on the mentions in self.mention_queue&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        # TODO: only handle a certain number of mentions at a time?&lt;br /&gt;
        for mention in iter(self.state[&#039;mention_queue&#039;]):&lt;br /&gt;
            prefix = self.get_mention_prefix(mention)&lt;br /&gt;
            self.on_mention(mention, prefix)&lt;br /&gt;
            self.state[&#039;mention_queue&#039;].remove(mention)&lt;br /&gt;
&lt;br /&gt;
            if self.config[&#039;autofav_mentions&#039;]:&lt;br /&gt;
                self.favorite_tweet(mention)&lt;br /&gt;
&lt;br /&gt;
            #time.sleep(self.config[&#039;reply_interval&#039;])&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def get_mention_prefix(self, tweet):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Returns a string of users to @-mention when responding to a tweet.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        mention_back = [&#039;@&#039; + tweet.author.screen_name]&lt;br /&gt;
        mention_back += [s for s in re.split(&#039;[^@\w]&#039;, tweet.text) if len(s) &amp;gt; 2 and s[0] == &#039;@&#039; and s[1:] != self.screen_name]&lt;br /&gt;
&lt;br /&gt;
        if self.config[&#039;reply_followers_only&#039;]:&lt;br /&gt;
            mention_back = [s for s in mention_back if s[1:] in self.state[&#039;followers&#039;] or s == &#039;@&#039; + tweet.author.screen_name]&lt;br /&gt;
&lt;br /&gt;
        return &#039; &#039;.join(mention_back)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _check_mentions(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Checks mentions and loads most recent tweets into the mention queue&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        if self._ignore_method(self.on_mention):&lt;br /&gt;
            logging.debug(&#039;Ignoring mentions&#039;)&lt;br /&gt;
            return&lt;br /&gt;
&lt;br /&gt;
        try:&lt;br /&gt;
            current_mentions = self.api.mentions_timeline(since_id=self.state[&#039;last_mention_id&#039;], count=100)&lt;br /&gt;
&lt;br /&gt;
            # direct mentions only?&lt;br /&gt;
            if self.config[&#039;reply_direct_mention_only&#039;]:&lt;br /&gt;
                current_mentions = [t for t in current_mentions if re.split(&#039;[^@\w]&#039;, t.text)[0] == &#039;@&#039; + self.screen_name]&lt;br /&gt;
&lt;br /&gt;
            if len(current_mentions) != 0:&lt;br /&gt;
                self.state[&#039;last_mention_id&#039;] = current_mentions[0].id&lt;br /&gt;
            &lt;br /&gt;
            self.state[&#039;last_mention_time&#039;] = time.time()&lt;br /&gt;
&lt;br /&gt;
            self.state[&#039;mention_queue&#039;] += reversed(current_mentions)&lt;br /&gt;
&lt;br /&gt;
            logging.info(&#039;Mentions updated ({} retrieved, {} total in queue)&#039;.format(len(current_mentions), len(self.state[&#039;mention_queue&#039;])))&lt;br /&gt;
&lt;br /&gt;
        except tweepy.TweepError as e:&lt;br /&gt;
            self._log_tweepy_error(&#039;Can\&#039;t retrieve mentions&#039;, e)&lt;br /&gt;
&lt;br /&gt;
        except IncompleteRead as e:&lt;br /&gt;
            self.log(&#039;Incomplete read error -- skipping mentions update&#039;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _check_timeline(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Checks timeline and loads most recent tweets into recent timeline&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        if self._ignore_method(self.on_timeline):&lt;br /&gt;
            logging.debug(&#039;Ignoring timeline&#039;)&lt;br /&gt;
            return&lt;br /&gt;
&lt;br /&gt;
        try:&lt;br /&gt;
            current_timeline = self.api.home_timeline(count=200, since_id=self.state[&#039;last_timeline_id&#039;])&lt;br /&gt;
&lt;br /&gt;
            # remove my tweets&lt;br /&gt;
            current_timeline = [t for t in current_timeline if t.author.screen_name.lower() != self.screen_name.lower()]&lt;br /&gt;
&lt;br /&gt;
            # remove all tweets mentioning me&lt;br /&gt;
            current_timeline = [t for t in current_timeline if not re.search(&#039;@&#039;+self.screen_name, t.text, flags=re.IGNORECASE)]&lt;br /&gt;
&lt;br /&gt;
            if self.config[&#039;ignore_timeline_mentions&#039;]:&lt;br /&gt;
                # remove all tweets with mentions (heuristically)&lt;br /&gt;
                current_timeline = [t for t in current_timeline if &#039;@&#039; not in t.text]&lt;br /&gt;
&lt;br /&gt;
            if len(current_timeline) != 0:&lt;br /&gt;
                self.state[&#039;last_timeline_id&#039;] = current_timeline[0].id&lt;br /&gt;
            &lt;br /&gt;
            self.state[&#039;last_timeline_time&#039;] = time.time()&lt;br /&gt;
&lt;br /&gt;
            self.state[&#039;recent_timeline&#039;] = list(reversed(current_timeline))&lt;br /&gt;
&lt;br /&gt;
            logging.info(&#039;Timeline updated ({} retrieved)&#039;.format(len(current_timeline)))&lt;br /&gt;
&lt;br /&gt;
        except tweepy.TweepError as e:&lt;br /&gt;
            self._log_tweepy_error(&#039;Can\&#039;t retrieve timeline&#039;, e)&lt;br /&gt;
&lt;br /&gt;
        except IncompleteRead as e:&lt;br /&gt;
            self.log(&#039;Incomplete read error -- skipping timeline update&#039;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _check_followers(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Checks followers.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        logging.info(&amp;quot;Checking for new followers...&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
        try:&lt;br /&gt;
            self.state[&#039;new_followers&#039;] = [f_id for f_id in self.api.followers_ids(self.id) if f_id not in self.state[&#039;followers&#039;]]&lt;br /&gt;
&lt;br /&gt;
            self.config[&#039;last_follow_check&#039;] = time.time()&lt;br /&gt;
&lt;br /&gt;
        except tweepy.TweepError as e:&lt;br /&gt;
            self._log_tweepy_error(&#039;Can\&#039;t update followers&#039;, e)&lt;br /&gt;
&lt;br /&gt;
        except IncompleteRead as e:&lt;br /&gt;
            self.log(&#039;Incomplete read error -- skipping followers update&#039;)&lt;br /&gt;
&lt;br /&gt;
            &lt;br /&gt;
    def _handle_followers(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Handles new followers.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        for f_id in self.state[&#039;new_followers&#039;]:&lt;br /&gt;
            self.on_follow(f_id)&lt;br /&gt;
&lt;br /&gt;
    def register_custom_handler(self, action, interval):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Register a custom action to run at some interval.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        handler = {}&lt;br /&gt;
&lt;br /&gt;
        handler[&#039;action&#039;] = action&lt;br /&gt;
        handler[&#039;interval&#039;] = interval&lt;br /&gt;
        handler[&#039;last_run&#039;] = 0&lt;br /&gt;
&lt;br /&gt;
        self.custom_handlers.append(handler)&lt;br /&gt;
&lt;br /&gt;
    def run(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Runs the bot! This probably shouldn&#039;t be in a &amp;quot;while True&amp;quot; lol.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        while True:&lt;br /&gt;
            &lt;br /&gt;
            # check followers every 15 minutes&lt;br /&gt;
            #if self.autofollow and (time.time() - self.last_follow_check) &amp;gt; (15 * 60): &lt;br /&gt;
            if self.state[&#039;last_follow_check&#039;] &amp;gt; (15 * 60): &lt;br /&gt;
                self._check_followers()&lt;br /&gt;
                self._handle_followers()&lt;br /&gt;
&lt;br /&gt;
            # check mentions every minute-ish&lt;br /&gt;
            #if self.reply_to_mentions and (time.time() - self.last_mention_time) &amp;gt; 60:&lt;br /&gt;
            if (time.time() - self.state[&#039;last_mention_time&#039;]) &amp;gt; 60:&lt;br /&gt;
                self._check_mentions()&lt;br /&gt;
                self._handle_mentions()&lt;br /&gt;
&lt;br /&gt;
            # tweet to timeline&lt;br /&gt;
            #if self.reply_to_timeline and (time.time() - self.last_mention_time) &amp;gt; 60:&lt;br /&gt;
            if (time.time() - self.state[&#039;last_timeline_time&#039;]) &amp;gt; 60:&lt;br /&gt;
                self._check_timeline()&lt;br /&gt;
                self._handle_timeline()&lt;br /&gt;
&lt;br /&gt;
            # tweet to timeline on the correct interval&lt;br /&gt;
            if (time.time() - self.state[&#039;last_tweet_time&#039;]) &amp;gt; self.config[&#039;tweet_interval&#039;]:&lt;br /&gt;
                self.on_scheduled_tweet()&lt;br /&gt;
&lt;br /&gt;
                # TODO: maybe this should only run if the above is successful...&lt;br /&gt;
                if self.config[&#039;tweet_interval_range&#039;] is not None:&lt;br /&gt;
                    self.config[&#039;tweet_interval&#039;] = random.randint(*self.config[&#039;tweet_interval_range&#039;])&lt;br /&gt;
&lt;br /&gt;
                self.log(&amp;quot;Next tweet in {} seconds&amp;quot;.format(self.config[&#039;tweet_interval&#039;]))&lt;br /&gt;
                self.state[&#039;last_tweet_time&#039;] = time.time()&lt;br /&gt;
&lt;br /&gt;
            # run custom action&lt;br /&gt;
            for handler in self.custom_handlers:&lt;br /&gt;
                if (time.time() - handler[&#039;last_run&#039;]) &amp;gt; handler[&#039;interval&#039;]:&lt;br /&gt;
                    handler[&#039;action&#039;]()&lt;br /&gt;
                    handler[&#039;last_run&#039;] = time.time()&lt;br /&gt;
&lt;br /&gt;
            # save current state&lt;br /&gt;
            self._save_state()&lt;br /&gt;
&lt;br /&gt;
            logging.info(&amp;quot;Sleeping for a bit...&amp;quot;)&lt;br /&gt;
            time.sleep(30)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
class FileStorage(object):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Default storage adapter.&lt;br /&gt;
&lt;br /&gt;
    Adapters must implement two methods: read(name) and write(name).&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def read(self, name):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Return an IO-like object that will produce binary data when read from.&lt;br /&gt;
        If nothing is stored under the given name, raise IOError.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        filename = self._get_filename(name)&lt;br /&gt;
        if os.path.exists(filename):&lt;br /&gt;
            logging.debug(&amp;quot;Reading from {}&amp;quot;.format(filename))&lt;br /&gt;
        else:&lt;br /&gt;
            logging.debug(&amp;quot;{} doesn&#039;t exist&amp;quot;.format(filename))&lt;br /&gt;
        return open(filename, &#039;rb&#039;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def write(self, name):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Return an IO-like object that will store binary data written to it.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        filename = self._get_filename(name)&lt;br /&gt;
        if os.path.exists(filename):&lt;br /&gt;
            logging.debug(&amp;quot;Overwriting {}&amp;quot;.format(filename))&lt;br /&gt;
        else:&lt;br /&gt;
            logging.debug(&amp;quot;Creating {}&amp;quot;.format(filename))&lt;br /&gt;
        return open(filename, &#039;wb&#039;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _get_filename(self, name):&lt;br /&gt;
        return &#039;{}_state.pkl&#039;.format(name)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>SebastianStang</name></author>
	</entry>
	<entry>
		<id>https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=GMU:Bots_%27n%27_Plots/Sebastian_Stang&amp;diff=72825</id>
		<title>GMU:Bots &#039;n&#039; Plots/Sebastian Stang</title>
		<link rel="alternate" type="text/html" href="https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=GMU:Bots_%27n%27_Plots/Sebastian_Stang&amp;diff=72825"/>
		<updated>2015-06-17T15:53:53Z</updated>

		<summary type="html">&lt;p&gt;SebastianStang: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;( )   ( )&amp;lt;br/&amp;gt;&lt;br /&gt;
(&amp;gt;•.•&amp;lt;)&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;nowiki&amp;gt;  (&amp;quot;)    (&amp;quot;)&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== @_pil_bot_ ==&lt;br /&gt;
&lt;br /&gt;
=== pilbot.py ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
# -*- coding: utf-8 -*- #&lt;br /&gt;
&lt;br /&gt;
from twitterbot import TwitterBot&lt;br /&gt;
import keys&lt;br /&gt;
&lt;br /&gt;
from PIL import Image&lt;br /&gt;
from sys import argv&lt;br /&gt;
from random import random&lt;br /&gt;
&lt;br /&gt;
from pileval import PILEval&lt;br /&gt;
&lt;br /&gt;
class PilBot(TwitterBot):&lt;br /&gt;
    &lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        TwitterBot.__init__(self)&lt;br /&gt;
        self.pillermann = PILEval()&lt;br /&gt;
        &lt;br /&gt;
    def bot_init(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Initialize and configure the bot &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
        ############################&lt;br /&gt;
        # REQUIRED: LOGIN DETAILS! #&lt;br /&gt;
        ############################&lt;br /&gt;
        self.config[&#039;api_key&#039;] = keys.consumer_key  &lt;br /&gt;
        self.config[&#039;api_secret&#039;] = keys.consumer_secret&lt;br /&gt;
        self.config[&#039;access_key&#039;] = keys.access_token&lt;br /&gt;
        self.config[&#039;access_secret&#039;] = keys.access_token_secret&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        ######################################&lt;br /&gt;
        # SEMI-OPTIONAL: OTHER CONFIG STUFF! #&lt;br /&gt;
        ######################################&lt;br /&gt;
&lt;br /&gt;
        # how often to tweet, in seconds&lt;br /&gt;
        self.config[&#039;tweet_interval&#039;] = 1 * 5     # default: 1 minutes&lt;br /&gt;
&lt;br /&gt;
        # use this to define a (min, max) random range of how often to tweet&lt;br /&gt;
        # e.g., self.config[&#039;tweet_interval_range&#039;] = (5*60, 10*60) # tweets every 5-10 minutes&lt;br /&gt;
        self.config[&#039;tweet_interval_range&#039;] = None&lt;br /&gt;
&lt;br /&gt;
        # only reply to tweets that specifically mention the bot&lt;br /&gt;
        self.config[&#039;reply_direct_mention_only&#039;] = True&lt;br /&gt;
&lt;br /&gt;
        # only include bot followers (and original tweeter) in @-replies&lt;br /&gt;
        self.config[&#039;reply_followers_only&#039;] = False&lt;br /&gt;
&lt;br /&gt;
        # fav any tweets that mention this bot?&lt;br /&gt;
        self.config[&#039;autofav_mentions&#039;] = False&lt;br /&gt;
&lt;br /&gt;
        # fav any tweets containing these keywords?&lt;br /&gt;
        self.config[&#039;autofav_keywords&#039;] = []&lt;br /&gt;
&lt;br /&gt;
        # follow back all followers?&lt;br /&gt;
        self.config[&#039;autofollow&#039;] = False&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def on_scheduled_tweet(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Make a public tweet to the bot&#039;s own timeline. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        # We might take senteces from somewhere and tweet them on a regular basis ...&lt;br /&gt;
        pass # don&#039;t do anything here ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def on_mention(self, tweet, prefix):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Actions to take when a mention is received. &amp;quot;&amp;quot;&amp;quot;     &lt;br /&gt;
        text = tweet.text.split()[1]&lt;br /&gt;
        self.action(text)&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
    def on_timeline(self, tweet, prefix):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Actions to take on a timeline tweet. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        pass&lt;br /&gt;
    &lt;br /&gt;
    def action(self, code):&lt;br /&gt;
        result = self.pillermann.eval(code)&lt;br /&gt;
        &lt;br /&gt;
        if result: # post the image&lt;br /&gt;
            self.post_tweet(code, media=&amp;quot;img.png&amp;quot;, file=self.pillermann.get_file())        &lt;br /&gt;
        else: # post the error&lt;br /&gt;
            self.post_tweet(self.pillermann.last_error)        &lt;br /&gt;
    &lt;br /&gt;
if __name__ == &#039;__main__&#039;:&lt;br /&gt;
&lt;br /&gt;
    bot = PilBot()&lt;br /&gt;
    &lt;br /&gt;
    if len(argv) == 1:&lt;br /&gt;
        bot.run()&lt;br /&gt;
    else:     &lt;br /&gt;
        tweet_text = &amp;quot;text((300, 400), &#039;botsNplots&#039;, fill=&#039;#ff0&#039;)&amp;quot;        &lt;br /&gt;
        bot.action(tweet_text)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== pileval.py ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
from PIL import Image, ImageDraw&lt;br /&gt;
&lt;br /&gt;
from os import path&lt;br /&gt;
from io import BytesIO&lt;br /&gt;
&lt;br /&gt;
class PILEval():&lt;br /&gt;
    &lt;br /&gt;
    def __init__(self, filename=&#039;test.png&#039;):&lt;br /&gt;
        self.filename = filename&lt;br /&gt;
        try:&lt;br /&gt;
            self.image = Image.open(filename)&lt;br /&gt;
        except FileNotFoundError as e:&lt;br /&gt;
            self.image = Image.new(&amp;quot;RGBA&amp;quot;, (640, 480), &#039;white&#039;)        &lt;br /&gt;
        self.last_error = &#039;&#039;&lt;br /&gt;
        &lt;br /&gt;
    def __repr__(self):&lt;br /&gt;
        return &amp;quot;&amp;lt;PILEval: %s&amp;gt;&amp;quot; % self.filename&lt;br /&gt;
            &lt;br /&gt;
    def eval(self, code):&lt;br /&gt;
        draw = ImageDraw.Draw(self.image, &amp;quot;RGBA&amp;quot;)        &lt;br /&gt;
        code = &amp;quot;draw.%s&amp;quot; % code&lt;br /&gt;
&lt;br /&gt;
        try:&lt;br /&gt;
            code_obj = compile(code, &#039;&amp;lt;string&amp;gt;&#039;, &#039;exec&#039;)&lt;br /&gt;
            exec(code_obj)&lt;br /&gt;
        except Exception as e:&lt;br /&gt;
            self.last_error = str(e)&lt;br /&gt;
            return False            &lt;br /&gt;
        &lt;br /&gt;
        self.image.save(self.filename)&lt;br /&gt;
   &lt;br /&gt;
        return True&lt;br /&gt;
    &lt;br /&gt;
    def get_file(self):&lt;br /&gt;
        img = Image.open(self.filename)&lt;br /&gt;
        file = BytesIO()&lt;br /&gt;
        img.save(file, format=&#039;PNG&#039;)&lt;br /&gt;
        return file&lt;br /&gt;
        &lt;br /&gt;
if __name__ == &#039;__main__&#039;:&lt;br /&gt;
    # some q&#039;n&#039;dirty tests&lt;br /&gt;
    print(&amp;quot;main&amp;quot;)&lt;br /&gt;
    pillermann = PILEval()&lt;br /&gt;
&lt;br /&gt;
    # will work&lt;br /&gt;
    # result = pillermann.eval(&amp;quot;rectangle([20, 10, 120, 200], fill=&#039;#f00&#039;)&amp;quot;)&lt;br /&gt;
    # if result:&lt;br /&gt;
    #     print(&#039;good&#039;)&lt;br /&gt;
        &lt;br /&gt;
    # will also work&lt;br /&gt;
    # result = pillermann.eval(&amp;quot;ellipse([200, 50, 500, 500], fill=&#039;#def&#039;)&amp;quot;)&lt;br /&gt;
    # result = pillermann.eval(&amp;quot;point((123, 234), fill=&#039;#345&#039;)&amp;quot;)&lt;br /&gt;
    # result = pillermann.eval(&amp;quot;line([23, 34, 345, 456], fill=&#039;#eca&#039;, width=10)&amp;quot;)&lt;br /&gt;
    # result = pillermann.eval(&amp;quot;arc([500, 10, 600, 440], 10, 95, fill=&#039;#3f9&#039;)&amp;quot;)&lt;br /&gt;
    # result = pillermann.eval(&amp;quot;text((22, 202), &#039;_pil_bot_!&#039;, fill=&#039;#a4f&#039;)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
    result = pillermann.eval(&amp;quot;text((100, 100), text=code, fill=&#039;#000&#039;)&amp;quot;)&lt;br /&gt;
    if result:&lt;br /&gt;
        print(&amp;quot;ok&amp;quot;)&lt;br /&gt;
    else:&lt;br /&gt;
        print(pillermann.last_error)&lt;br /&gt;
    # will yield a compiler error&lt;br /&gt;
    # result = pillermann.eval(&amp;quot;rectangle([20, 10, 120, 200], fill=&#039;#f00&#039;&amp;quot;)&lt;br /&gt;
    # if not result:&lt;br /&gt;
    #     print(&amp;quot;error result: %s&amp;quot; % pillermann.last_error)      &lt;br /&gt;
&lt;br /&gt;
    # f = pillermann.get_file()&lt;br /&gt;
    # print(f)&lt;br /&gt;
    &lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== twitterbot/bot.py === &lt;br /&gt;
&lt;br /&gt;
adapts thricedotted&#039;s (http://lczzz.me) twitterbot for python 3&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
import os&lt;br /&gt;
import codecs&lt;br /&gt;
import json&lt;br /&gt;
import logging&lt;br /&gt;
import tweepy&lt;br /&gt;
import time&lt;br /&gt;
import re&lt;br /&gt;
import random&lt;br /&gt;
import pickle&lt;br /&gt;
&lt;br /&gt;
def ignore(method):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Use the @ignore decorator on TwitterBot methods you wish to leave&lt;br /&gt;
    unimplemented, such as on_timeline and on_mention.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    method.not_implemented = True&lt;br /&gt;
    return method&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
class TwitterBot:&lt;br /&gt;
&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        self.config = {}&lt;br /&gt;
&lt;br /&gt;
        self.custom_handlers = []&lt;br /&gt;
&lt;br /&gt;
        self.config[&#039;reply_direct_mention_only&#039;] = False&lt;br /&gt;
        self.config[&#039;reply_followers_only&#039;] = True&lt;br /&gt;
&lt;br /&gt;
        self.config[&#039;autofav_mentions&#039;] = False&lt;br /&gt;
        self.config[&#039;autofav_keywords&#039;] = []&lt;br /&gt;
&lt;br /&gt;
        self.config[&#039;autofollow&#039;] = False&lt;br /&gt;
&lt;br /&gt;
        self.config[&#039;tweet_interval&#039;] = 30 * 60&lt;br /&gt;
        self.config[&#039;tweet_interval_range&#039;] = None&lt;br /&gt;
&lt;br /&gt;
        self.config[&#039;reply_interval&#039;] = 10&lt;br /&gt;
        self.config[&#039;reply_interval_range&#039;] = None&lt;br /&gt;
&lt;br /&gt;
        self.config[&#039;ignore_timeline_mentions&#039;] = True&lt;br /&gt;
&lt;br /&gt;
        self.config[&#039;logging_level&#039;] = logging.DEBUG&lt;br /&gt;
        self.config[&#039;storage&#039;] = FileStorage()&lt;br /&gt;
&lt;br /&gt;
        self.state = {}&lt;br /&gt;
&lt;br /&gt;
        # call the custom initialization&lt;br /&gt;
        self.bot_init()&lt;br /&gt;
&lt;br /&gt;
        auth = tweepy.OAuthHandler(self.config[&#039;api_key&#039;], self.config[&#039;api_secret&#039;])&lt;br /&gt;
        auth.set_access_token(self.config[&#039;access_key&#039;], self.config[&#039;access_secret&#039;])&lt;br /&gt;
        self.api = tweepy.API(auth)&lt;br /&gt;
&lt;br /&gt;
        self.id = self.api.me().id&lt;br /&gt;
        self.screen_name = self.api.me().screen_name&lt;br /&gt;
&lt;br /&gt;
        logging.basicConfig(format=&#039;%(asctime)s | %(levelname)s: %(message)s&#039;, datefmt=&#039;%m/%d/%Y %I:%M:%S %p&#039;, &lt;br /&gt;
            filename=self.screen_name + &#039;.log&#039;,&lt;br /&gt;
            level=self.config[&#039;logging_level&#039;])&lt;br /&gt;
&lt;br /&gt;
        logging.info(&#039;Initializing bot...&#039;)&lt;br /&gt;
&lt;br /&gt;
        try:&lt;br /&gt;
            with self.config[&#039;storage&#039;].read(self.screen_name) as f:                &lt;br /&gt;
                self.state = pickle.load(f)&lt;br /&gt;
                &lt;br /&gt;
        except IOError:&lt;br /&gt;
            self.state[&#039;last_timeline_id&#039;] = 1&lt;br /&gt;
            self.state[&#039;last_mention_id&#039;] = 1&lt;br /&gt;
&lt;br /&gt;
            self.state[&#039;last_timeline_time&#039;] = 0&lt;br /&gt;
            self.state[&#039;last_mention_time&#039;] = 0&lt;br /&gt;
&lt;br /&gt;
            self.state[&#039;last_tweet_id&#039;] = 1&lt;br /&gt;
            self.state[&#039;last_tweet_time&#039;] = 1&lt;br /&gt;
&lt;br /&gt;
            self.state[&#039;last_reply_id&#039;] = 0&lt;br /&gt;
            self.state[&#039;last_reply_time&#039;] = 0&lt;br /&gt;
&lt;br /&gt;
            self.state[&#039;recent_timeline&#039;] = []&lt;br /&gt;
            self.state[&#039;mention_queue&#039;] = []&lt;br /&gt;
&lt;br /&gt;
        self.state[&#039;friends&#039;] = self.api.friends_ids(self.id)&lt;br /&gt;
        self.state[&#039;followers&#039;] = self.api.followers_ids(self.id)&lt;br /&gt;
        self.state[&#039;new_followers&#039;] = []&lt;br /&gt;
        self.state[&#039;last_follow_check&#039;] = 0&lt;br /&gt;
&lt;br /&gt;
        logging.info(&#039;Bot initialized!&#039;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def bot_init(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Initialize custom state values for your bot.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        raise NotImplementedError(&amp;quot;You MUST have bot_init() implemented in your bot! What have you DONE!&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def log(self, message, level=logging.INFO):&lt;br /&gt;
        if level == logging.ERROR:&lt;br /&gt;
            logging.error(message)&lt;br /&gt;
        else:&lt;br /&gt;
            logging.info(message)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _log_tweepy_error(self, message, e):&lt;br /&gt;
        try:&lt;br /&gt;
            e_message = e.message[0][&#039;message&#039;]&lt;br /&gt;
            code = e.message[0][&#039;code&#039;]&lt;br /&gt;
            self.log(&amp;quot;{}: {} ({})&amp;quot;.format(message, e_message, code), level=logging.ERROR)&lt;br /&gt;
        except:&lt;br /&gt;
            self.log(message, e)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _tweet_url(self, tweet):&lt;br /&gt;
        return &amp;quot;http://twitter.com/&amp;quot; + tweet.author.screen_name + &amp;quot;/status/&amp;quot; + str(tweet.id)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _save_state(self):&lt;br /&gt;
        with self.config[&#039;storage&#039;].write(self.screen_name) as f:&lt;br /&gt;
            pickle.dump(self.state, f)&lt;br /&gt;
            self.log(&#039;Bot state saved&#039;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def on_scheduled_tweet(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Post a general tweet to own timeline.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        #self.post_tweet(text)&lt;br /&gt;
        raise NotImplementedError(&amp;quot;You need to implement this to tweet to timeline (or pass if you don&#039;t want to)!&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def on_mention(self, tweet, prefix):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Perform some action upon receiving a mention.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        #self.post_tweet(text)&lt;br /&gt;
        raise NotImplementedError(&amp;quot;You need to implement this to reply to/fav mentions (or pass if you don&#039;t want to)!&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def on_timeline(self, tweet, prefix):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Perform some action on a tweet on the timeline.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        #self.post_tweet(text)&lt;br /&gt;
        raise NotImplementedError(&amp;quot;You need to implement this to reply to/fav timeline tweets (or pass if you don&#039;t want to)!&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def on_follow(self, f_id):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Perform some action when followed.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        if self.config[&#039;autofollow&#039;]:&lt;br /&gt;
            try:&lt;br /&gt;
                self.api.create_friendship(f_id, follow=True)&lt;br /&gt;
                self.state[&#039;friends&#039;].append(f_id)&lt;br /&gt;
                logging.info(&#039;Followed user id {}&#039;.format(f_id))&lt;br /&gt;
            except tweepy.TweepError as e:&lt;br /&gt;
                self._log_tweepy_error(&#039;Unable to follow user&#039;, e)&lt;br /&gt;
&lt;br /&gt;
            time.sleep(3)&lt;br /&gt;
&lt;br /&gt;
        self.state[&#039;followers&#039;].append(f_id)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def post_tweet(self, text, reply_to=None, media=None, file=None):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Post a tweet containing the text. &lt;br /&gt;
        If you provide a filename the file will be added to the tweet.&lt;br /&gt;
        You can also provide the filedata, for example when using dynamically created images.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
        kwargs = { &amp;quot;status&amp;quot;: text }&lt;br /&gt;
        args = []&lt;br /&gt;
&lt;br /&gt;
        if media is not None:&lt;br /&gt;
            cmd = self.api.update_with_media&lt;br /&gt;
            args.insert(0, media)&lt;br /&gt;
&lt;br /&gt;
            if file is not None:&lt;br /&gt;
                kwargs[&amp;quot;file&amp;quot;] = file&lt;br /&gt;
        else:&lt;br /&gt;
            cmd = self.api.update_status&lt;br /&gt;
&lt;br /&gt;
        try:&lt;br /&gt;
            self.log(&#039;Tweeting &amp;quot;{}&amp;quot;&#039;.format(text))&lt;br /&gt;
            if reply_to:&lt;br /&gt;
                self.log(&amp;quot;-- Responding to status {}&amp;quot;.format(self._tweet_url(reply_to)))&lt;br /&gt;
                kwargs[&#039;in_reply_to_status_id&#039;] = reply_to.id&lt;br /&gt;
            else:&lt;br /&gt;
                self.log(&amp;quot;-- Posting to own timeline&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
            tweet = cmd(*args, **kwargs)&lt;br /&gt;
            self.log(&#039;Status posted at {}&#039;.format(self._tweet_url(tweet)))&lt;br /&gt;
            return True&lt;br /&gt;
&lt;br /&gt;
        except tweepy.TweepError as e:&lt;br /&gt;
            self._log_tweepy_error(&#039;Can\&#039;t post status&#039;, e)&lt;br /&gt;
            return False&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def favorite_tweet(self, tweet):&lt;br /&gt;
        try:&lt;br /&gt;
            logging.info(&#039;Faving &#039; + self._tweet_url(tweet))&lt;br /&gt;
            self.api.create_favorite(tweet.id)&lt;br /&gt;
&lt;br /&gt;
        except tweepy.TweepError as e:&lt;br /&gt;
            self._log_tweepy_error(&#039;Can\&#039;t fav status&#039;, e)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _ignore_method(self, method):&lt;br /&gt;
        return hasattr(method, &#039;not_implemented&#039;) and method.not_implemented&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _handle_timeline(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Reads the latest tweets in the bots timeline and perform some action.&lt;br /&gt;
        self.recent_timeline&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        for tweet in self.state[&#039;recent_timeline&#039;]:&lt;br /&gt;
            prefix = self.get_mention_prefix(tweet)&lt;br /&gt;
            self.on_timeline(tweet, prefix)&lt;br /&gt;
&lt;br /&gt;
            words = tweet.text.lower().split()&lt;br /&gt;
            if any(w in words for w in self.config[&#039;autofav_keywords&#039;]):&lt;br /&gt;
                self.favorite_tweet(tweet)&lt;br /&gt;
&lt;br /&gt;
            #time.sleep(self.config[&#039;reply_interval&#039;])&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _handle_mentions(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Performs some action on the mentions in self.mention_queue&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        # TODO: only handle a certain number of mentions at a time?&lt;br /&gt;
        for mention in iter(self.state[&#039;mention_queue&#039;]):&lt;br /&gt;
            prefix = self.get_mention_prefix(mention)&lt;br /&gt;
            self.on_mention(mention, prefix)&lt;br /&gt;
            self.state[&#039;mention_queue&#039;].remove(mention)&lt;br /&gt;
&lt;br /&gt;
            if self.config[&#039;autofav_mentions&#039;]:&lt;br /&gt;
                self.favorite_tweet(mention)&lt;br /&gt;
&lt;br /&gt;
            #time.sleep(self.config[&#039;reply_interval&#039;])&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def get_mention_prefix(self, tweet):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Returns a string of users to @-mention when responding to a tweet.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        mention_back = [&#039;@&#039; + tweet.author.screen_name]&lt;br /&gt;
        mention_back += [s for s in re.split(&#039;[^@\w]&#039;, tweet.text) if len(s) &amp;gt; 2 and s[0] == &#039;@&#039; and s[1:] != self.screen_name]&lt;br /&gt;
&lt;br /&gt;
        if self.config[&#039;reply_followers_only&#039;]:&lt;br /&gt;
            mention_back = [s for s in mention_back if s[1:] in self.state[&#039;followers&#039;] or s == &#039;@&#039; + tweet.author.screen_name]&lt;br /&gt;
&lt;br /&gt;
        return &#039; &#039;.join(mention_back)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _check_mentions(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Checks mentions and loads most recent tweets into the mention queue&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        if self._ignore_method(self.on_mention):&lt;br /&gt;
            logging.debug(&#039;Ignoring mentions&#039;)&lt;br /&gt;
            return&lt;br /&gt;
&lt;br /&gt;
        try:&lt;br /&gt;
            current_mentions = self.api.mentions_timeline(since_id=self.state[&#039;last_mention_id&#039;], count=100)&lt;br /&gt;
&lt;br /&gt;
            # direct mentions only?&lt;br /&gt;
            if self.config[&#039;reply_direct_mention_only&#039;]:&lt;br /&gt;
                current_mentions = [t for t in current_mentions if re.split(&#039;[^@\w]&#039;, t.text)[0] == &#039;@&#039; + self.screen_name]&lt;br /&gt;
&lt;br /&gt;
            if len(current_mentions) != 0:&lt;br /&gt;
                self.state[&#039;last_mention_id&#039;] = current_mentions[0].id&lt;br /&gt;
            &lt;br /&gt;
            self.state[&#039;last_mention_time&#039;] = time.time()&lt;br /&gt;
&lt;br /&gt;
            self.state[&#039;mention_queue&#039;] += reversed(current_mentions)&lt;br /&gt;
&lt;br /&gt;
            logging.info(&#039;Mentions updated ({} retrieved, {} total in queue)&#039;.format(len(current_mentions), len(self.state[&#039;mention_queue&#039;])))&lt;br /&gt;
&lt;br /&gt;
        except tweepy.TweepError as e:&lt;br /&gt;
            self._log_tweepy_error(&#039;Can\&#039;t retrieve mentions&#039;, e)&lt;br /&gt;
&lt;br /&gt;
        except IncompleteRead as e:&lt;br /&gt;
            self.log(&#039;Incomplete read error -- skipping mentions update&#039;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _check_timeline(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Checks timeline and loads most recent tweets into recent timeline&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        if self._ignore_method(self.on_timeline):&lt;br /&gt;
            logging.debug(&#039;Ignoring timeline&#039;)&lt;br /&gt;
            return&lt;br /&gt;
&lt;br /&gt;
        try:&lt;br /&gt;
            current_timeline = self.api.home_timeline(count=200, since_id=self.state[&#039;last_timeline_id&#039;])&lt;br /&gt;
&lt;br /&gt;
            # remove my tweets&lt;br /&gt;
            current_timeline = [t for t in current_timeline if t.author.screen_name.lower() != self.screen_name.lower()]&lt;br /&gt;
&lt;br /&gt;
            # remove all tweets mentioning me&lt;br /&gt;
            current_timeline = [t for t in current_timeline if not re.search(&#039;@&#039;+self.screen_name, t.text, flags=re.IGNORECASE)]&lt;br /&gt;
&lt;br /&gt;
            if self.config[&#039;ignore_timeline_mentions&#039;]:&lt;br /&gt;
                # remove all tweets with mentions (heuristically)&lt;br /&gt;
                current_timeline = [t for t in current_timeline if &#039;@&#039; not in t.text]&lt;br /&gt;
&lt;br /&gt;
            if len(current_timeline) != 0:&lt;br /&gt;
                self.state[&#039;last_timeline_id&#039;] = current_timeline[0].id&lt;br /&gt;
            &lt;br /&gt;
            self.state[&#039;last_timeline_time&#039;] = time.time()&lt;br /&gt;
&lt;br /&gt;
            self.state[&#039;recent_timeline&#039;] = list(reversed(current_timeline))&lt;br /&gt;
&lt;br /&gt;
            logging.info(&#039;Timeline updated ({} retrieved)&#039;.format(len(current_timeline)))&lt;br /&gt;
&lt;br /&gt;
        except tweepy.TweepError as e:&lt;br /&gt;
            self._log_tweepy_error(&#039;Can\&#039;t retrieve timeline&#039;, e)&lt;br /&gt;
&lt;br /&gt;
        except IncompleteRead as e:&lt;br /&gt;
            self.log(&#039;Incomplete read error -- skipping timeline update&#039;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _check_followers(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Checks followers.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        logging.info(&amp;quot;Checking for new followers...&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
        try:&lt;br /&gt;
            self.state[&#039;new_followers&#039;] = [f_id for f_id in self.api.followers_ids(self.id) if f_id not in self.state[&#039;followers&#039;]]&lt;br /&gt;
&lt;br /&gt;
            self.config[&#039;last_follow_check&#039;] = time.time()&lt;br /&gt;
&lt;br /&gt;
        except tweepy.TweepError as e:&lt;br /&gt;
            self._log_tweepy_error(&#039;Can\&#039;t update followers&#039;, e)&lt;br /&gt;
&lt;br /&gt;
        except IncompleteRead as e:&lt;br /&gt;
            self.log(&#039;Incomplete read error -- skipping followers update&#039;)&lt;br /&gt;
&lt;br /&gt;
            &lt;br /&gt;
    def _handle_followers(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Handles new followers.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        for f_id in self.state[&#039;new_followers&#039;]:&lt;br /&gt;
            self.on_follow(f_id)&lt;br /&gt;
&lt;br /&gt;
    def register_custom_handler(self, action, interval):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Register a custom action to run at some interval.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        handler = {}&lt;br /&gt;
&lt;br /&gt;
        handler[&#039;action&#039;] = action&lt;br /&gt;
        handler[&#039;interval&#039;] = interval&lt;br /&gt;
        handler[&#039;last_run&#039;] = 0&lt;br /&gt;
&lt;br /&gt;
        self.custom_handlers.append(handler)&lt;br /&gt;
&lt;br /&gt;
    def run(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Runs the bot! This probably shouldn&#039;t be in a &amp;quot;while True&amp;quot; lol.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        while True:&lt;br /&gt;
            &lt;br /&gt;
            # check followers every 15 minutes&lt;br /&gt;
            #if self.autofollow and (time.time() - self.last_follow_check) &amp;gt; (15 * 60): &lt;br /&gt;
            if self.state[&#039;last_follow_check&#039;] &amp;gt; (15 * 60): &lt;br /&gt;
                self._check_followers()&lt;br /&gt;
                self._handle_followers()&lt;br /&gt;
&lt;br /&gt;
            # check mentions every minute-ish&lt;br /&gt;
            #if self.reply_to_mentions and (time.time() - self.last_mention_time) &amp;gt; 60:&lt;br /&gt;
            if (time.time() - self.state[&#039;last_mention_time&#039;]) &amp;gt; 60:&lt;br /&gt;
                self._check_mentions()&lt;br /&gt;
                self._handle_mentions()&lt;br /&gt;
&lt;br /&gt;
            # tweet to timeline&lt;br /&gt;
            #if self.reply_to_timeline and (time.time() - self.last_mention_time) &amp;gt; 60:&lt;br /&gt;
            if (time.time() - self.state[&#039;last_timeline_time&#039;]) &amp;gt; 60:&lt;br /&gt;
                self._check_timeline()&lt;br /&gt;
                self._handle_timeline()&lt;br /&gt;
&lt;br /&gt;
            # tweet to timeline on the correct interval&lt;br /&gt;
            if (time.time() - self.state[&#039;last_tweet_time&#039;]) &amp;gt; self.config[&#039;tweet_interval&#039;]:&lt;br /&gt;
                self.on_scheduled_tweet()&lt;br /&gt;
&lt;br /&gt;
                # TODO: maybe this should only run if the above is successful...&lt;br /&gt;
                if self.config[&#039;tweet_interval_range&#039;] is not None:&lt;br /&gt;
                    self.config[&#039;tweet_interval&#039;] = random.randint(*self.config[&#039;tweet_interval_range&#039;])&lt;br /&gt;
&lt;br /&gt;
                self.log(&amp;quot;Next tweet in {} seconds&amp;quot;.format(self.config[&#039;tweet_interval&#039;]))&lt;br /&gt;
                self.state[&#039;last_tweet_time&#039;] = time.time()&lt;br /&gt;
&lt;br /&gt;
            # run custom action&lt;br /&gt;
            for handler in self.custom_handlers:&lt;br /&gt;
                if (time.time() - handler[&#039;last_run&#039;]) &amp;gt; handler[&#039;interval&#039;]:&lt;br /&gt;
                    handler[&#039;action&#039;]()&lt;br /&gt;
                    handler[&#039;last_run&#039;] = time.time()&lt;br /&gt;
&lt;br /&gt;
            # save current state&lt;br /&gt;
            self._save_state()&lt;br /&gt;
&lt;br /&gt;
            logging.info(&amp;quot;Sleeping for a bit...&amp;quot;)&lt;br /&gt;
            time.sleep(30)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
class FileStorage(object):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Default storage adapter.&lt;br /&gt;
&lt;br /&gt;
    Adapters must implement two methods: read(name) and write(name).&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def read(self, name):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Return an IO-like object that will produce binary data when read from.&lt;br /&gt;
        If nothing is stored under the given name, raise IOError.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        filename = self._get_filename(name)&lt;br /&gt;
        if os.path.exists(filename):&lt;br /&gt;
            logging.debug(&amp;quot;Reading from {}&amp;quot;.format(filename))&lt;br /&gt;
        else:&lt;br /&gt;
            logging.debug(&amp;quot;{} doesn&#039;t exist&amp;quot;.format(filename))&lt;br /&gt;
        return open(filename, &#039;rb&#039;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def write(self, name):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Return an IO-like object that will store binary data written to it.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        filename = self._get_filename(name)&lt;br /&gt;
        if os.path.exists(filename):&lt;br /&gt;
            logging.debug(&amp;quot;Overwriting {}&amp;quot;.format(filename))&lt;br /&gt;
        else:&lt;br /&gt;
            logging.debug(&amp;quot;Creating {}&amp;quot;.format(filename))&lt;br /&gt;
        return open(filename, &#039;wb&#039;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    def _get_filename(self, name):&lt;br /&gt;
        return &#039;{}_state.pkl&#039;.format(name)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>SebastianStang</name></author>
	</entry>
	<entry>
		<id>https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=Template:PythonBooks&amp;diff=72556</id>
		<title>Template:PythonBooks</title>
		<link rel="alternate" type="text/html" href="https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=Template:PythonBooks&amp;diff=72556"/>
		<updated>2015-06-02T16:55:37Z</updated>

		<summary type="html">&lt;p&gt;SebastianStang: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Python ===&lt;br /&gt;
* Beazley, David: &#039;&#039;Python Cookbook&#039;&#039; ISBN 978-1449340377 &lt;br /&gt;
* Chan, Jamie: &#039;&#039;Learn Python in One Day and Learn it Well&#039;&#039; ISBN 978-1506094380&lt;br /&gt;
* Blum, Richard: &#039;&#039;Python Programming for Raspberry Pi&#039;&#039; ISBN 978-0789752055 &lt;br /&gt;
* Zelle, John: &#039;&#039;Python Programming: An Introduction to Computer Science&#039;&#039; ISBN 978-1590282410&lt;br /&gt;
* Gries, Paul: &#039;&#039;Practical Programming: An Introduction to Computer Science Using Python 3&#039;&#039; ISBN 978-1937785451 &lt;br /&gt;
&lt;br /&gt;
=== Bots n Plots ===&lt;br /&gt;
* Russell, Matthew A.: &#039;&#039;Mining the Social Web: Data Mining Facebook, Twitter, LinkedIn, Google+, GitHub, and More&#039;&#039; ISBN 978-1449367619&lt;br /&gt;
* Perkins, Jacob: &#039;&#039;Python Text Processing with NLTK 2.0 Cookbook&#039;&#039; ISBN  978-1849513609&lt;br /&gt;
* Bird, Steven: &#039;&#039;Natural Language Processing with Python&#039;&#039; ISBN 978-0596516499&lt;br /&gt;
* Craven, Dr. Paul Vincent: &#039;&#039;Program Arcade Games: With Python and Pygame&#039;&#039; ISBN 978-1500825966&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Eval Nature ===&lt;br /&gt;
* Solem, Jan Erik: &#039;&#039;Programming Computer Vision with Python: Tools and algorithms for analyzing images&#039;&#039; ISBN 978-1449316549&lt;br /&gt;
* Ravishankar Chityala &amp;amp; Sridevi Pudipeddi: &#039;&#039;Image Processing and Acquisition Using Python (Chapman &amp;amp; Hall/CRC Mathematical and Computational Imaging Sc)&#039;&#039; ISBN 978-1466583757&lt;br /&gt;
* Downey, Allen B.: &#039;&#039;Think Complexity: Complexity Science and Computational Modeling&#039;&#039; ISBN 978-1449314637&lt;br /&gt;
* Mitchell, Melanie: &#039;&#039;Complexity: A Guided Tour&#039;&#039; ISBN 978-0199798100&lt;/div&gt;</summary>
		<author><name>SebastianStang</name></author>
	</entry>
	<entry>
		<id>https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=Template:PythonBooks&amp;diff=72555</id>
		<title>Template:PythonBooks</title>
		<link rel="alternate" type="text/html" href="https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=Template:PythonBooks&amp;diff=72555"/>
		<updated>2015-06-02T16:53:36Z</updated>

		<summary type="html">&lt;p&gt;SebastianStang: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Python ===&lt;br /&gt;
* Beazley, David: &#039;&#039;Python Cookbook&#039;&#039; ISBN 978-1449340377 &lt;br /&gt;
* Chan, Jamie: &#039;&#039;Learn Python in One Day and Learn it Well&#039;&#039; ISBN 978-1506094380&lt;br /&gt;
* Blum, Richard: &#039;&#039;Python Programming for Raspberry Pi&#039;&#039; ISBN 978-0789752055 &lt;br /&gt;
* Zelle, John: &#039;&#039;Python Programming: An Introduction to Computer Science&#039;&#039; ISBN 978-1590282410&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Bots n Plots ===&lt;br /&gt;
* Russell, Matthew A.: &#039;&#039;Mining the Social Web: Data Mining Facebook, Twitter, LinkedIn, Google+, GitHub, and More&#039;&#039; ISBN 978-1449367619&lt;br /&gt;
* Perkins, Jacob: &#039;&#039;Python Text Processing with NLTK 2.0 Cookbook&#039;&#039; ISBN  978-1849513609&lt;br /&gt;
* Bird, Steven: &#039;&#039;Natural Language Processing with Python&#039;&#039; ISBN 978-0596516499&lt;br /&gt;
* Craven, Dr. Paul Vincent: &#039;&#039;Program Arcade Games: With Python and Pygame&#039;&#039; ISBN 978-1500825966&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Eval Nature ===&lt;br /&gt;
* Solem, Jan Erik: &#039;&#039;Programming Computer Vision with Python: Tools and algorithms for analyzing images&#039;&#039; ISBN 978-1449316549&lt;br /&gt;
* Ravishankar Chityala &amp;amp; Sridevi Pudipeddi: &#039;&#039;Image Processing and Acquisition Using Python (Chapman &amp;amp; Hall/CRC Mathematical and Computational Imaging Sc)&#039;&#039; ISBN 978-1466583757&lt;br /&gt;
* Downey, Allen B.: &#039;&#039;Think Complexity: Complexity Science and Computational Modeling&#039;&#039; ISBN 978-1449314637&lt;/div&gt;</summary>
		<author><name>SebastianStang</name></author>
	</entry>
	<entry>
		<id>https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=Template:PythonBooks&amp;diff=72554</id>
		<title>Template:PythonBooks</title>
		<link rel="alternate" type="text/html" href="https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=Template:PythonBooks&amp;diff=72554"/>
		<updated>2015-06-02T16:52:37Z</updated>

		<summary type="html">&lt;p&gt;SebastianStang: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Python ===&lt;br /&gt;
* Beazley, David: &#039;&#039;Python Cookbook&#039;&#039; ISBN 978-1449340377 &lt;br /&gt;
* Chan, Jamie: &#039;&#039;Learn Python in One Day and Learn it Well&#039;&#039; ISBN 978-1506094380&lt;br /&gt;
* Blum, Richard: &#039;&#039;Python Programming for Raspberry Pi&#039;&#039; ISBN 978-0789752055 &lt;br /&gt;
* Zelle, John: &#039;&#039;Python Programming: An Introduction to Computer Science&#039;&#039; ISBN 978-1590282410&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Bots n Plots ===&lt;br /&gt;
* Russell, Matthew A.: &#039;&#039;Mining the Social Web: Data Mining Facebook, Twitter, LinkedIn, Google+, GitHub, and More&#039;&#039; ISBN 978-1449367619&lt;br /&gt;
* Perkins, Jacob: &#039;&#039;Python Text Processing with NLTK 2.0 Cookbook&#039;&#039; ISBN  978-1849513609&lt;br /&gt;
* Bird, Steven: &#039;&#039;Natural Language Processing with Python&#039;&#039; ISBN 978-0596516499&lt;br /&gt;
* Craven, Dr. Paul Vincent: &#039;&#039;Program Arcade Games: With Python and Pygame&#039;&#039; ISBN 978-1500825966&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Eval Nature ===&lt;br /&gt;
* Solem, Jan Erik: &#039;&#039;Programming Computer Vision with Python: Tools and algorithms for analyzing images&#039;&#039; ISBN 978-1449316549&lt;br /&gt;
* Ravishankar Chityala &amp;amp; Sridevi Pudipeddi: &#039;&#039;Image Processing and Acquisition Using Python (Chapman &amp;amp; Hall/CRC Mathematical and Computational Imaging Sc)&#039;&#039; ISBN 978-1466583757&lt;/div&gt;</summary>
		<author><name>SebastianStang</name></author>
	</entry>
	<entry>
		<id>https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=Template:PythonBooks&amp;diff=72553</id>
		<title>Template:PythonBooks</title>
		<link rel="alternate" type="text/html" href="https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=Template:PythonBooks&amp;diff=72553"/>
		<updated>2015-06-02T16:50:01Z</updated>

		<summary type="html">&lt;p&gt;SebastianStang: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Python ===&lt;br /&gt;
* Beazley, David: &#039;&#039;Python Cookbook&#039;&#039; ISBN 978-1449340377 &lt;br /&gt;
* Chan, Jamie: &#039;&#039;Learn Python in One Day and Learn it Well&#039;&#039; ISBN 978-1506094380&lt;br /&gt;
* Blum, Richard: &#039;&#039;Python Programming for Raspberry Pi&#039;&#039; ISBN 978-0789752055 &lt;br /&gt;
* Zelle, John: &#039;&#039;Python Programming: An Introduction to Computer Science&#039;&#039; ISBN 978-1590282410&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Bots n Plots ===&lt;br /&gt;
* Russell, Matthew A.: &#039;&#039;Mining the Social Web: Data Mining Facebook, Twitter, LinkedIn, Google+, GitHub, and More&#039;&#039; ISBN 978-1449367619&lt;br /&gt;
* Perkins, Jacob: &#039;&#039;Python Text Processing with NLTK 2.0 Cookbook&#039;&#039; ISBN  978-1849513609&lt;br /&gt;
* Bird, Steven: &#039;&#039;Natural Language Processing with Python&#039;&#039; ISBN 978-0596516499&lt;br /&gt;
* Craven, Dr. Paul Vincent: &#039;&#039;Program Arcade Games: With Python and Pygame&#039;&#039; ISBN 978-1500825966&lt;/div&gt;</summary>
		<author><name>SebastianStang</name></author>
	</entry>
	<entry>
		<id>https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=Template:PythonBooks&amp;diff=72552</id>
		<title>Template:PythonBooks</title>
		<link rel="alternate" type="text/html" href="https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=Template:PythonBooks&amp;diff=72552"/>
		<updated>2015-06-02T16:45:10Z</updated>

		<summary type="html">&lt;p&gt;SebastianStang: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Python ===&lt;br /&gt;
* Beazley, David: &#039;&#039;Python Cookbook&#039;&#039; ISBN 978-1449340377 &lt;br /&gt;
* Chan, Jamie: &#039;&#039;Learn Python in One Day and Learn it Well&#039;&#039; ISBN 978-1506094380&lt;br /&gt;
* Blum, Richard: &#039;&#039;Python Programming for Raspberry Pi&#039;&#039; ISBN 978-0789752055 &lt;br /&gt;
* Zelle, John: &#039;&#039;Python Programming: An Introduction to Computer Science&#039;&#039; ISBN 978-1590282410&lt;/div&gt;</summary>
		<author><name>SebastianStang</name></author>
	</entry>
	<entry>
		<id>https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=Template:PythonBooks&amp;diff=72551</id>
		<title>Template:PythonBooks</title>
		<link rel="alternate" type="text/html" href="https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=Template:PythonBooks&amp;diff=72551"/>
		<updated>2015-06-02T16:42:15Z</updated>

		<summary type="html">&lt;p&gt;SebastianStang: Created page with &amp;quot;* Beazley, David: &amp;#039;&amp;#039;Python Cookbook&amp;#039;&amp;#039; ISBN 978-1449340377  * Chan, Jamie: &amp;#039;&amp;#039;Learn Python in One Day and Learn it Well&amp;#039;&amp;#039; ISBN 978-1506094380 * Blum, Richard: &amp;#039;&amp;#039;Python Programming ...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;* Beazley, David: &#039;&#039;Python Cookbook&#039;&#039; ISBN 978-1449340377 &lt;br /&gt;
* Chan, Jamie: &#039;&#039;Learn Python in One Day and Learn it Well&#039;&#039; ISBN 978-1506094380&lt;br /&gt;
* Blum, Richard: &#039;&#039;Python Programming for Raspberry Pi&#039;&#039; ISBN 978-0789752055 &lt;br /&gt;
* Zelle, John: &#039;&#039;Python Programming: An Introduction to Computer Science&#039;&#039; ISBN 978-1590282410&lt;/div&gt;</summary>
		<author><name>SebastianStang</name></author>
	</entry>
	<entry>
		<id>https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=GMU:Bots_%27n%27_Plots/Sebastian_Stang&amp;diff=71570</id>
		<title>GMU:Bots &#039;n&#039; Plots/Sebastian Stang</title>
		<link rel="alternate" type="text/html" href="https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=GMU:Bots_%27n%27_Plots/Sebastian_Stang&amp;diff=71570"/>
		<updated>2015-04-22T09:05:17Z</updated>

		<summary type="html">&lt;p&gt;SebastianStang: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;( )   ( )&amp;lt;br/&amp;gt;&lt;br /&gt;
(&amp;gt;•.•&amp;lt;)&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;nowiki&amp;gt;  (&amp;quot;)    (&amp;quot;)&amp;lt;/nowiki&amp;gt;&lt;/div&gt;</summary>
		<author><name>SebastianStang</name></author>
	</entry>
	<entry>
		<id>https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=GMU:Bots_%27n%27_Plots&amp;diff=71569</id>
		<title>GMU:Bots &#039;n&#039; Plots</title>
		<link rel="alternate" type="text/html" href="https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=GMU:Bots_%27n%27_Plots&amp;diff=71569"/>
		<updated>2015-04-22T08:58:48Z</updated>

		<summary type="html">&lt;p&gt;SebastianStang: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Image:Bots-n-plots-maillardet-drawing.png|thumb|left|300px|&#039;&#039;Drawing by [[wikipedia:Maillardet&#039;s automaton|the Draughtsman-Writer Bot]] ca. 1800&#039;&#039;]] &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bots &#039;n&#039; Plots &amp;amp;mdash; Malen nach 0 und 1&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[:Category:Werkmodul|Werkmodul]]/[[:Category:Fachmodul|Fachmodul]]&amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;Lecturer:&#039;&#039; [[User:ms|Martin Schneider]]&amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;Credits:&#039;&#039; 6 [[ECTS]], 4 [[SWS]]&amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;Date:&#039;&#039; Wednesday 17:00 - 20:30 Uhr &amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;Venue:&#039;&#039; [[Marienstraße 7b]], Room 204&amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;First meeting:&#039;&#039; Wednesday, 15. Apr. 2015&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br style=&amp;quot;clear: both&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;NEWS&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;The Deadline has been extended by 24 hours :)&#039;&#039;&#039;&lt;br /&gt;
 &#039;&#039;&#039;You need to apply until 24:00 on April 13th, 2015.&#039;&#039;&#039;&lt;br /&gt;
 Applications after the deadline will not be taken into consideration.&lt;br /&gt;
 I will let you know if you made it onto the list on April 14th.&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;Follow [http://twitter.com/botsnplots @botsnplots] on Twitter!&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;The internet of things is infiltrating the social web.&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;A rich ecosystem of social bots is evolving right now.&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;Those bots range from the purposeful via the poetic to the philosophical.&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;And they are starting to talk to each other.&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the Bots &#039;n&#039; Plots course, we will create little robots that doodle and babble.  &amp;lt;br&amp;gt;&lt;br /&gt;
Our robots will be based in software, but we will also learn how to connect them with the physical world, using vision sound and serial connections.  &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the course of the class you will:&lt;br /&gt;
&lt;br /&gt;
* Learn programming with Python&lt;br /&gt;
* Process images and text&lt;br /&gt;
* Create generative drawings and poems&lt;br /&gt;
* Use APIs for the social web, including Twitter and Mediawiki&lt;br /&gt;
* Create your own software bots and make them talk to each other&lt;br /&gt;
&lt;br /&gt;
The course requires no previous programming experience.&lt;br /&gt;
&lt;br /&gt;
As a result of the course you will acquire fundamental programming skills that will enable you to take more advanced courses in the future. &amp;lt;br&amp;gt;&lt;br /&gt;
It is recommended to combine the course with the [[GMU:eval(nature)|eval(nature) module]].&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beschreibung ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Das Internet der Dinge erobert die sozialen Netzwerke.&#039;&#039;  &amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;Im Augenblick entwickelt sich ein vielfältiges Ökosystem sozialer Bots.&#039;&#039;  &amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;Die Bots decken ein weites Spektrum ab, vom Praktischen, über Poetisches bis hin zum Philosophischen.&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;Und sie fangen an miteinander zu kommunizieren...&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In &#039;&#039;Malen nach 0 und 1&#039;&#039; werden wir kleine Roboter erschaffen, die kritzeln und brabbeln. &amp;lt;br&amp;gt;&lt;br /&gt;
Unsere Roboter bestehen aus Nullen und Einsen, aber wir werden auch lernen sie mit der materiellen Welt zu verbinden, wobei Kameras, Töne und Kabel zum Einsatz kommen. &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Rahmen des Moduls werdet ihr:&lt;br /&gt;
&lt;br /&gt;
* Programmieren mit Python lernen&lt;br /&gt;
* Grafik und Text verarbeiten&lt;br /&gt;
* Generative Grafiken und Gedichte erzeugen&lt;br /&gt;
* APIs für Soziale Netzwerke nutzen, inkl. Twitter und Mediawiki&lt;br /&gt;
* Eigene Software-Bots entwickeln und sie dazu bringen miteinander zu reden.&lt;br /&gt;
&lt;br /&gt;
Dieses Modul erfordert keine Programmierkentnisse.&lt;br /&gt;
&lt;br /&gt;
Im Rahmen des Kurses werden grundlegende Programmierkentnisse vermittelt, die es ermöglichen in Zukunft fortgeschrittene Kurse zu belegen.&amp;lt;br&amp;gt;&lt;br /&gt;
Es wird empfohlen diesen Kurs mit dem Projekt-Modul [[GMU:eval(nature)|eval(nature)]] zu kombinieren.&lt;br /&gt;
&lt;br /&gt;
== Language ==&lt;br /&gt;
&lt;br /&gt;
The course will be in English, unless all participants are speaking German.&lt;br /&gt;
&lt;br /&gt;
== Eligible Participants ==&lt;br /&gt;
&lt;br /&gt;
Undergraduates and graduates enrolled in the faculties of:&lt;br /&gt;
&lt;br /&gt;
* Media Art/Design&lt;br /&gt;
* Visual Communication&lt;br /&gt;
&lt;br /&gt;
== Requirements ==&lt;br /&gt;
&lt;br /&gt;
* Burning Interest in Artificial Intelligence, Computer-Linguistics and Generative Design&lt;br /&gt;
* Previous course about the Internet of Things highly recommended&lt;br /&gt;
* Working knowledge of Web Technologies (HTML, CSS, Javascript ...)&lt;br /&gt;
* Time and Devotion for lots of coding homework, which may be frustrating at times&lt;br /&gt;
* Programming experience considered helpful&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
&lt;br /&gt;
The deadline to apply for this course is April 13th, 2015&lt;br /&gt;
&lt;br /&gt;
 You need to provide links to previous work to be elligible to this course.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;To:&#039;&#039;&#039; [[User:ms|Martin Schneider]]&amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Subject:&#039;&#039;&#039; Bots n Plots /// Application&amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Content:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# Please provide links to three of your visual, poetic or algorithmic works online.&lt;br /&gt;
# Please answer these questions:&lt;br /&gt;
#* What is your favourite bot on twitter?&lt;br /&gt;
#* What kind of bot would you like to be able to create at the end of the course?&lt;br /&gt;
# Please tell us about you:&lt;br /&gt;
#* program and semester (Studienprogramm und Fachsemester)&lt;br /&gt;
#* matriculation number (Matrikelnummer)&lt;br /&gt;
#* Valid email address @uni-weimar.de (no other mailing addresses will be accepted) [[SCC-Services#E-Mail|Why?]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
 This is the outline of the course.&lt;br /&gt;
 The details may still be subject to change.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Theme !! Topic !!  Date &lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | [[/Part1|Part 1: Python]]&lt;br /&gt;
| 01: Programming I&lt;br /&gt;
| 15. April 2015&lt;br /&gt;
|-&lt;br /&gt;
| 02: Programming II&lt;br /&gt;
| 22. April 2015&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;4&amp;quot; | [[/Part2|Part 2: Plotter Bots]]&lt;br /&gt;
| 03: Drawing and Plotting I&lt;br /&gt;
| 29. April 2015&lt;br /&gt;
|-&lt;br /&gt;
| 04: Drawing and Plotting II&lt;br /&gt;
| 06. May 2015&lt;br /&gt;
|-&lt;br /&gt;
| 05: Image Processing I&lt;br /&gt;
| 13. May 2015&lt;br /&gt;
|-&lt;br /&gt;
| 06: Image Processing II&lt;br /&gt;
| 20. May 2015&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;3&amp;quot; | [[/Part3|Part 3: Chatter Bots]]&lt;br /&gt;
| 07: Language Processing I&lt;br /&gt;
| 27. May 2015&lt;br /&gt;
|-&lt;br /&gt;
| 08: Language Processing II&lt;br /&gt;
| 03. June 2015&lt;br /&gt;
|-&lt;br /&gt;
| 09: Generating Poetry&lt;br /&gt;
| 10. June 2015&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;4&amp;quot; | [[/Part4|Part 4: Social Bots]]&lt;br /&gt;
| 10: Twitter Bots I&lt;br /&gt;
| 17. June 2015&lt;br /&gt;
|-&lt;br /&gt;
| 11: Twitter Bots II&lt;br /&gt;
| 24. June 2015&lt;br /&gt;
|-&lt;br /&gt;
| 12: Wiki Bots I&lt;br /&gt;
| 01. July 2015&lt;br /&gt;
|-&lt;br /&gt;
| 13: Wiki Bots II&lt;br /&gt;
| 08. July 2015&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Evaluation ==&lt;br /&gt;
&lt;br /&gt;
* Presence and active participation (required)&lt;br /&gt;
* Punctual and complete submission of homework (required)&lt;br /&gt;
&lt;br /&gt;
* 50% Technical and aesthetic execution of the final bot project&lt;br /&gt;
* 20% Emergent behaviour from making the robots talk&lt;br /&gt;
* 20% Documentation of the final project&lt;br /&gt;
* 10% Contribution to the MediaWiki&lt;br /&gt;
&lt;br /&gt;
== Participants ==&lt;br /&gt;
&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Sebastian Stang|Sebastian Stang]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Maria Degand|Maria Degand]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Klaus Kraemer|Klaus Kraemer]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Angelica Sohn|Angelica Sohn]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Luis Hurtarte|Luis Hurtarte]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Apasri Titatarn|Apasri Titatarn]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Jan Dropmann|Jan Dropmann]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Azucena Sanchez|Azucena Sanchez]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Natalia Martínez|Natalia Martínez]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/smin kim|Smin Kim]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/minsoo hwang|minsoo Hwang]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Christopher Marx|Christopher Marx]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Shubhra Bhatt|Shubhra Bhatt]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Constantin Oestreich|Constantin Oestreich]]&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Here are a couple of links that should give you an idea of where this course is heading ...&lt;br /&gt;
&lt;br /&gt;
=== Learning Python ===&lt;br /&gt;
* [http://www.codecademy.com/en/tracks/python Learn Python] at Code Academy&lt;br /&gt;
* [http://www.diveintopython3.net Dive Into Python 3]&lt;br /&gt;
&lt;br /&gt;
=== Image Processing ===&lt;br /&gt;
* [http://software-carpentry.org/v4/media/ Multimedia Programming] by Software Carpentry&lt;br /&gt;
=== Video Processing ===&lt;br /&gt;
* [https://zulko.github.io/moviepy/ MoviePy] by Zulko&lt;br /&gt;
* [https://zulko.github.io/blog/2014/09/20/vector-animations-with-python/ Vector Animations with Python]&lt;br /&gt;
=== Audio ===&lt;br /&gt;
==== IO ====&lt;br /&gt;
* [http://people.csail.mit.edu/hubert/pyaudio/ PyAudio] realtime IO, PortAudio&lt;br /&gt;
* [https://github.com/thestk/rtaudio/tree/master/contrib/python/pyrtaudio PyRtAudio] realtime IO, RtAudio&lt;br /&gt;
* [http://www.ar.media.kyoto-u.ac.jp/members/david/softwares/audiolab/ audiolab] audio file IO, libsndfile&lt;br /&gt;
==== Analysis and Synthesis ====&lt;br /&gt;
* [http://www.cerlsoundgroup.org/Loris/ Loris]&lt;br /&gt;
* [http://musickit.sourceforge.net MusicKit]&lt;br /&gt;
* [http://sndobj.sourceforge.net SndObject]&lt;br /&gt;
==== Plotting ====&lt;br /&gt;
* [http://matplotlib.org matplotlib]&lt;br /&gt;
==== Number Crunching ====&lt;br /&gt;
* [http://docs.scipy.org/doc/ scipy and numpy]&lt;br /&gt;
&lt;br /&gt;
=== Language Processing ===&lt;br /&gt;
* [http://h6o6.com/2013/03/using-python-and-the-nltk-to-find-haikus-in-the-public-twitter-stream/ Using Python and the NLTK to find Haikus in the public twitter stream]&lt;br /&gt;
* [http://dhconnelly.com/paip-python/docs/paip/eliza.html Eliza.py]&lt;br /&gt;
=== Twitter ===&lt;br /&gt;
* [https://zulko.github.io/blog/2014/07/26/a-tweets-controlled-python-script/|A Python Script controlled via Twitter]&lt;br /&gt;
=== Contribution by Students ===&lt;br /&gt;
* [http://www.belfasttelegraph.co.uk/technology/robot-that-bought-mdma-passport-and-baseball-cap-released-by-authorities-31150495.html &amp;quot;Random Darknet Shopper&amp;quot; bot released from prison]&lt;br /&gt;
&lt;br /&gt;
== Literature ==&lt;br /&gt;
* Bird, Steven: &#039;&#039;Natural Language Processing with Python&#039;&#039; ISBN 978-0596516499 ([http://www.nltk.org/book/ online])&lt;br /&gt;
* Lobin, Hennig:&#039;&#039;Computerlinguistik und Texttechnologie&#039;&#039; ISBN 978-3825232825 &lt;br /&gt;
* Russel, Mathew: &#039;&#039;Mining the Social Web&#039;&#039; ISBN 978-1449367619&lt;br /&gt;
* Segaran, Toby&#039;&#039;: Programming Collective Intelligence&#039;&#039; ISBN 978-0596529321&lt;br /&gt;
&lt;br /&gt;
=== Python ===&lt;br /&gt;
This is a list of recommended books for the Python language:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;!-- {{PythonBooks}} --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Beazley, David: &#039;&#039;Python Cookbook&#039;&#039; ISBN 978-1449340377 &lt;br /&gt;
* Blum, Richard: &#039;&#039;Python Programming for Raspberry Pi&#039;&#039; ISBN 978-0789752055 &lt;br /&gt;
* Chan, Jamie: &#039;&#039;Learn Python in One Day and Learn it Well&#039;&#039; ISBN 978-1506094380&lt;br /&gt;
* Zelle, John: &#039;&#039;Python Programming: An Introduction to Computer Science&#039;&#039; ISBN 978-1590282410 ([http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.111.6062 online])&lt;br /&gt;
&lt;br /&gt;
=== Further Reading ===&lt;br /&gt;
There are a couple of books in the Semesterapparat of [[Society of Networked Things]], that you might want to flip trough.&lt;br /&gt;
&lt;br /&gt;
[[Category:SS15]]&lt;br /&gt;
[[Category:Werkmodul]]&lt;br /&gt;
[[Category:Fachmodul]]&lt;br /&gt;
[[Category:Martin Schneider]]&lt;br /&gt;
[[Category:Python]]&lt;br /&gt;
[[Category:Gestaltung]]&lt;br /&gt;
[[Category:Design]]&lt;br /&gt;
[[Category:Poetry]]&lt;br /&gt;
[[Category:Generative Gestaltung]]&lt;br /&gt;
[[Category:Robots]]&lt;/div&gt;</summary>
		<author><name>SebastianStang</name></author>
	</entry>
	<entry>
		<id>https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=GMU:Bots_%27n%27_Plots&amp;diff=71567</id>
		<title>GMU:Bots &#039;n&#039; Plots</title>
		<link rel="alternate" type="text/html" href="https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=GMU:Bots_%27n%27_Plots&amp;diff=71567"/>
		<updated>2015-04-22T08:41:26Z</updated>

		<summary type="html">&lt;p&gt;SebastianStang: added audio file io&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Image:Bots-n-plots-maillardet-drawing.png|thumb|left|300px|&#039;&#039;Drawing by [[wikipedia:Maillardet&#039;s automaton|the Draughtsman-Writer Bot]] ca. 1800&#039;&#039;]] &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bots &#039;n&#039; Plots &amp;amp;mdash; Malen nach 0 und 1&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[:Category:Werkmodul|Werkmodul]]/[[:Category:Fachmodul|Fachmodul]]&amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;Lecturer:&#039;&#039; [[User:ms|Martin Schneider]]&amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;Credits:&#039;&#039; 6 [[ECTS]], 4 [[SWS]]&amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;Date:&#039;&#039; Wednesday 17:00 - 20:30 Uhr &amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;Venue:&#039;&#039; [[Marienstraße 7b]], Room 204&amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;First meeting:&#039;&#039; Wednesday, 15. Apr. 2015&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br style=&amp;quot;clear: both&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;NEWS&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;The Deadline has been extended by 24 hours :)&#039;&#039;&#039;&lt;br /&gt;
 &#039;&#039;&#039;You need to apply until 24:00 on April 13th, 2015.&#039;&#039;&#039;&lt;br /&gt;
 Applications after the deadline will not be taken into consideration.&lt;br /&gt;
 I will let you know if you made it onto the list on April 14th.&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;Follow [http://twitter.com/botsnplots @botsnplots] on Twitter!&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;The internet of things is infiltrating the social web.&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;A rich ecosystem of social bots is evolving right now.&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;Those bots range from the purposeful via the poetic to the philosophical.&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;And they are starting to talk to each other.&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the Bots &#039;n&#039; Plots course, we will create little robots that doodle and babble.  &amp;lt;br&amp;gt;&lt;br /&gt;
Our robots will be based in software, but we will also learn how to connect them with the physical world, using vision sound and serial connections.  &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the course of the class you will:&lt;br /&gt;
&lt;br /&gt;
* Learn programming with Python&lt;br /&gt;
* Process images and text&lt;br /&gt;
* Create generative drawings and poems&lt;br /&gt;
* Use APIs for the social web, including Twitter and Mediawiki&lt;br /&gt;
* Create your own software bots and make them talk to each other&lt;br /&gt;
&lt;br /&gt;
The course requires no previous programming experience.&lt;br /&gt;
&lt;br /&gt;
As a result of the course you will acquire fundamental programming skills that will enable you to take more advanced courses in the future. &amp;lt;br&amp;gt;&lt;br /&gt;
It is recommended to combine the course with the [[GMU:eval(nature)|eval(nature) module]].&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beschreibung ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Das Internet der Dinge erobert die sozialen Netzwerke.&#039;&#039;  &amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;Im Augenblick entwickelt sich ein vielfältiges Ökosystem sozialer Bots.&#039;&#039;  &amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;Die Bots decken ein weites Spektrum ab, vom Praktischen, über Poetisches bis hin zum Philosophischen.&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;Und sie fangen an miteinander zu kommunizieren...&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In &#039;&#039;Malen nach 0 und 1&#039;&#039; werden wir kleine Roboter erschaffen, die kritzeln und brabbeln. &amp;lt;br&amp;gt;&lt;br /&gt;
Unsere Roboter bestehen aus Nullen und Einsen, aber wir werden auch lernen sie mit der materiellen Welt zu verbinden, wobei Kameras, Töne und Kabel zum Einsatz kommen. &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Rahmen des Moduls werdet ihr:&lt;br /&gt;
&lt;br /&gt;
* Programmieren mit Python lernen&lt;br /&gt;
* Grafik und Text verarbeiten&lt;br /&gt;
* Generative Grafiken und Gedichte erzeugen&lt;br /&gt;
* APIs für Soziale Netzwerke nutzen, inkl. Twitter und Mediawiki&lt;br /&gt;
* Eigene Software-Bots entwickeln und sie dazu bringen miteinander zu reden.&lt;br /&gt;
&lt;br /&gt;
Dieses Modul erfordert keine Programmierkentnisse.&lt;br /&gt;
&lt;br /&gt;
Im Rahmen des Kurses werden grundlegende Programmierkentnisse vermittelt, die es ermöglichen in Zukunft fortgeschrittene Kurse zu belegen.&amp;lt;br&amp;gt;&lt;br /&gt;
Es wird empfohlen diesen Kurs mit dem Projekt-Modul [[GMU:eval(nature)|eval(nature)]] zu kombinieren.&lt;br /&gt;
&lt;br /&gt;
== Language ==&lt;br /&gt;
&lt;br /&gt;
The course will be in English, unless all participants are speaking German.&lt;br /&gt;
&lt;br /&gt;
== Eligible Participants ==&lt;br /&gt;
&lt;br /&gt;
Undergraduates and graduates enrolled in the faculties of:&lt;br /&gt;
&lt;br /&gt;
* Media Art/Design&lt;br /&gt;
* Visual Communication&lt;br /&gt;
&lt;br /&gt;
== Requirements ==&lt;br /&gt;
&lt;br /&gt;
* Burning Interest in Artificial Intelligence, Computer-Linguistics and Generative Design&lt;br /&gt;
* Previous course about the Internet of Things highly recommended&lt;br /&gt;
* Working knowledge of Web Technologies (HTML, CSS, Javascript ...)&lt;br /&gt;
* Time and Devotion for lots of coding homework, which may be frustrating at times&lt;br /&gt;
* Programming experience considered helpful&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
&lt;br /&gt;
The deadline to apply for this course is April 13th, 2015&lt;br /&gt;
&lt;br /&gt;
 You need to provide links to previous work to be elligible to this course.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;To:&#039;&#039;&#039; [[User:ms|Martin Schneider]]&amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Subject:&#039;&#039;&#039; Bots n Plots /// Application&amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Content:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# Please provide links to three of your visual, poetic or algorithmic works online.&lt;br /&gt;
# Please answer these questions:&lt;br /&gt;
#* What is your favourite bot on twitter?&lt;br /&gt;
#* What kind of bot would you like to be able to create at the end of the course?&lt;br /&gt;
# Please tell us about you:&lt;br /&gt;
#* program and semester (Studienprogramm und Fachsemester)&lt;br /&gt;
#* matriculation number (Matrikelnummer)&lt;br /&gt;
#* Valid email address @uni-weimar.de (no other mailing addresses will be accepted) [[SCC-Services#E-Mail|Why?]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
 This is the outline of the course.&lt;br /&gt;
 The details may still be subject to change.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Theme !! Topic !!  Date &lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | [[/Part1|Part 1: Python]]&lt;br /&gt;
| 01: Programming I&lt;br /&gt;
| 15. April 2015&lt;br /&gt;
|-&lt;br /&gt;
| 02: Programming II&lt;br /&gt;
| 22. April 2015&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;4&amp;quot; | [[/Part2|Part 2: Plotter Bots]]&lt;br /&gt;
| 03: Drawing and Plotting I&lt;br /&gt;
| 29. April 2015&lt;br /&gt;
|-&lt;br /&gt;
| 04: Drawing and Plotting II&lt;br /&gt;
| 06. May 2015&lt;br /&gt;
|-&lt;br /&gt;
| 05: Image Processing I&lt;br /&gt;
| 13. May 2015&lt;br /&gt;
|-&lt;br /&gt;
| 06: Image Processing II&lt;br /&gt;
| 20. May 2015&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;3&amp;quot; | [[/Part3|Part 3: Chatter Bots]]&lt;br /&gt;
| 07: Language Processing I&lt;br /&gt;
| 27. May 2015&lt;br /&gt;
|-&lt;br /&gt;
| 08: Language Processing II&lt;br /&gt;
| 03. June 2015&lt;br /&gt;
|-&lt;br /&gt;
| 09: Generating Poetry&lt;br /&gt;
| 10. June 2015&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;4&amp;quot; | [[/Part4|Part 4: Social Bots]]&lt;br /&gt;
| 10: Twitter Bots I&lt;br /&gt;
| 17. June 2015&lt;br /&gt;
|-&lt;br /&gt;
| 11: Twitter Bots II&lt;br /&gt;
| 24. June 2015&lt;br /&gt;
|-&lt;br /&gt;
| 12: Wiki Bots I&lt;br /&gt;
| 01. July 2015&lt;br /&gt;
|-&lt;br /&gt;
| 13: Wiki Bots II&lt;br /&gt;
| 08. July 2015&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Evaluation ==&lt;br /&gt;
&lt;br /&gt;
* Presence and active participation (required)&lt;br /&gt;
* Punctual and complete submission of homework (required)&lt;br /&gt;
&lt;br /&gt;
* 50% Technical and aesthetic execution of the final bot project&lt;br /&gt;
* 20% Emergent behaviour from making the robots talk&lt;br /&gt;
* 20% Documentation of the final project&lt;br /&gt;
* 10% Contribution to the MediaWiki&lt;br /&gt;
&lt;br /&gt;
== Participants ==&lt;br /&gt;
&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Sebastian Stang|Sebastian Stang]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Maria Degand|Maria Degand]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Klaus Kraemer|Klaus Kraemer]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Angelica Sohn|Angelica Sohn]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Luis Hurtarte|Luis Hurtarte]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Apasri Titatarn|Apasri Titatarn]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Jan Dropmann|Jan Dropmann]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Azucena Sanchez|Azucena Sanchez]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Natalia Martínez|Natalia Martínez]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/smin kim|Smin Kim]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/minsoo hwang|minsoo Hwang]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Christopher Marx|Christopher Marx]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Shubhra Bhatt|Shubhra Bhatt]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Constantin Oestreich|Constantin Oestreich]]&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Here are a couple of links that should give you an idea of where this course is heading ...&lt;br /&gt;
&lt;br /&gt;
=== Learning Python ===&lt;br /&gt;
* [http://www.codecademy.com/en/tracks/python Learn Python] at Code Academy&lt;br /&gt;
* [http://www.diveintopython3.net Dive Into Python 3]&lt;br /&gt;
&lt;br /&gt;
=== Image Processing ===&lt;br /&gt;
* [http://software-carpentry.org/v4/media/ Multimedia Programming] by Software Carpentry&lt;br /&gt;
=== Video Processing ===&lt;br /&gt;
* [https://zulko.github.io/moviepy/ MoviePy] by Zulko&lt;br /&gt;
* [https://zulko.github.io/blog/2014/09/20/vector-animations-with-python/ Vector Animations with Python]&lt;br /&gt;
=== Audio Processing ===&lt;br /&gt;
====IO====&lt;br /&gt;
* [http://people.csail.mit.edu/hubert/pyaudio/ PyAudio] realtime IO, PortAudio&lt;br /&gt;
* [https://github.com/thestk/rtaudio/tree/master/contrib/python/pyrtaudio PyRtAudio] realtime IO, RtAudio&lt;br /&gt;
* [http://www.ar.media.kyoto-u.ac.jp/members/david/softwares/audiolab/ audiolab] audio file IO, libsndfile&lt;br /&gt;
=== Language Processing ===&lt;br /&gt;
* [http://h6o6.com/2013/03/using-python-and-the-nltk-to-find-haikus-in-the-public-twitter-stream/ Using Python and the NLTK to find Haikus in the public twitter stream]&lt;br /&gt;
* [http://dhconnelly.com/paip-python/docs/paip/eliza.html Eliza.py]&lt;br /&gt;
=== Twitter ===&lt;br /&gt;
* [https://zulko.github.io/blog/2014/07/26/a-tweets-controlled-python-script/|A Python Script controlled via Twitter]&lt;br /&gt;
=== Contribution by Students ===&lt;br /&gt;
* [http://www.belfasttelegraph.co.uk/technology/robot-that-bought-mdma-passport-and-baseball-cap-released-by-authorities-31150495.html &amp;quot;Random Darknet Shopper&amp;quot; bot released from prison]&lt;br /&gt;
&lt;br /&gt;
== Literature ==&lt;br /&gt;
* Bird, Steven: &#039;&#039;Natural Language Processing with Python&#039;&#039; ISBN 978-0596516499 ([http://www.nltk.org/book/ online])&lt;br /&gt;
* Lobin, Hennig:&#039;&#039;Computerlinguistik und Texttechnologie&#039;&#039; ISBN 978-3825232825 &lt;br /&gt;
* Russel, Mathew: &#039;&#039;Mining the Social Web&#039;&#039; ISBN 978-1449367619&lt;br /&gt;
* Segaran, Toby&#039;&#039;: Programming Collective Intelligence&#039;&#039; ISBN 978-0596529321&lt;br /&gt;
&lt;br /&gt;
=== Python ===&lt;br /&gt;
This is a list of recommended books for the Python language:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;!-- {{PythonBooks}} --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Beazley, David: &#039;&#039;Python Cookbook&#039;&#039; ISBN 978-1449340377 &lt;br /&gt;
* Blum, Richard: &#039;&#039;Python Programming for Raspberry Pi&#039;&#039; ISBN 978-0789752055 &lt;br /&gt;
* Chan, Jamie: &#039;&#039;Learn Python in One Day and Learn it Well&#039;&#039; ISBN 978-1506094380&lt;br /&gt;
* Zelle, John: &#039;&#039;Python Programming: An Introduction to Computer Science&#039;&#039; ISBN 978-1590282410 ([http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.111.6062 online])&lt;br /&gt;
&lt;br /&gt;
=== Further Reading ===&lt;br /&gt;
There are a couple of books in the Semesterapparat of [[Society of Networked Things]], that you might want to flip trough.&lt;br /&gt;
&lt;br /&gt;
[[Category:SS15]]&lt;br /&gt;
[[Category:Werkmodul]]&lt;br /&gt;
[[Category:Fachmodul]]&lt;br /&gt;
[[Category:Martin Schneider]]&lt;br /&gt;
[[Category:Python]]&lt;br /&gt;
[[Category:Gestaltung]]&lt;br /&gt;
[[Category:Design]]&lt;br /&gt;
[[Category:Poetry]]&lt;br /&gt;
[[Category:Generative Gestaltung]]&lt;br /&gt;
[[Category:Robots]]&lt;/div&gt;</summary>
		<author><name>SebastianStang</name></author>
	</entry>
	<entry>
		<id>https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=GMU:Bots_%27n%27_Plots&amp;diff=71566</id>
		<title>GMU:Bots &#039;n&#039; Plots</title>
		<link rel="alternate" type="text/html" href="https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=GMU:Bots_%27n%27_Plots&amp;diff=71566"/>
		<updated>2015-04-22T08:34:15Z</updated>

		<summary type="html">&lt;p&gt;SebastianStang: added Audio-IO libraries&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Image:Bots-n-plots-maillardet-drawing.png|thumb|left|300px|&#039;&#039;Drawing by [[wikipedia:Maillardet&#039;s automaton|the Draughtsman-Writer Bot]] ca. 1800&#039;&#039;]] &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bots &#039;n&#039; Plots &amp;amp;mdash; Malen nach 0 und 1&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[:Category:Werkmodul|Werkmodul]]/[[:Category:Fachmodul|Fachmodul]]&amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;Lecturer:&#039;&#039; [[User:ms|Martin Schneider]]&amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;Credits:&#039;&#039; 6 [[ECTS]], 4 [[SWS]]&amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;Date:&#039;&#039; Wednesday 17:00 - 20:30 Uhr &amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;Venue:&#039;&#039; [[Marienstraße 7b]], Room 204&amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;First meeting:&#039;&#039; Wednesday, 15. Apr. 2015&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br style=&amp;quot;clear: both&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;NEWS&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;The Deadline has been extended by 24 hours :)&#039;&#039;&#039;&lt;br /&gt;
 &#039;&#039;&#039;You need to apply until 24:00 on April 13th, 2015.&#039;&#039;&#039;&lt;br /&gt;
 Applications after the deadline will not be taken into consideration.&lt;br /&gt;
 I will let you know if you made it onto the list on April 14th.&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;Follow [http://twitter.com/botsnplots @botsnplots] on Twitter!&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;The internet of things is infiltrating the social web.&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;A rich ecosystem of social bots is evolving right now.&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;Those bots range from the purposeful via the poetic to the philosophical.&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;And they are starting to talk to each other.&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the Bots &#039;n&#039; Plots course, we will create little robots that doodle and babble.  &amp;lt;br&amp;gt;&lt;br /&gt;
Our robots will be based in software, but we will also learn how to connect them with the physical world, using vision sound and serial connections.  &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the course of the class you will:&lt;br /&gt;
&lt;br /&gt;
* Learn programming with Python&lt;br /&gt;
* Process images and text&lt;br /&gt;
* Create generative drawings and poems&lt;br /&gt;
* Use APIs for the social web, including Twitter and Mediawiki&lt;br /&gt;
* Create your own software bots and make them talk to each other&lt;br /&gt;
&lt;br /&gt;
The course requires no previous programming experience.&lt;br /&gt;
&lt;br /&gt;
As a result of the course you will acquire fundamental programming skills that will enable you to take more advanced courses in the future. &amp;lt;br&amp;gt;&lt;br /&gt;
It is recommended to combine the course with the [[GMU:eval(nature)|eval(nature) module]].&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beschreibung ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Das Internet der Dinge erobert die sozialen Netzwerke.&#039;&#039;  &amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;Im Augenblick entwickelt sich ein vielfältiges Ökosystem sozialer Bots.&#039;&#039;  &amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;Die Bots decken ein weites Spektrum ab, vom Praktischen, über Poetisches bis hin zum Philosophischen.&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;Und sie fangen an miteinander zu kommunizieren...&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In &#039;&#039;Malen nach 0 und 1&#039;&#039; werden wir kleine Roboter erschaffen, die kritzeln und brabbeln. &amp;lt;br&amp;gt;&lt;br /&gt;
Unsere Roboter bestehen aus Nullen und Einsen, aber wir werden auch lernen sie mit der materiellen Welt zu verbinden, wobei Kameras, Töne und Kabel zum Einsatz kommen. &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Rahmen des Moduls werdet ihr:&lt;br /&gt;
&lt;br /&gt;
* Programmieren mit Python lernen&lt;br /&gt;
* Grafik und Text verarbeiten&lt;br /&gt;
* Generative Grafiken und Gedichte erzeugen&lt;br /&gt;
* APIs für Soziale Netzwerke nutzen, inkl. Twitter und Mediawiki&lt;br /&gt;
* Eigene Software-Bots entwickeln und sie dazu bringen miteinander zu reden.&lt;br /&gt;
&lt;br /&gt;
Dieses Modul erfordert keine Programmierkentnisse.&lt;br /&gt;
&lt;br /&gt;
Im Rahmen des Kurses werden grundlegende Programmierkentnisse vermittelt, die es ermöglichen in Zukunft fortgeschrittene Kurse zu belegen.&amp;lt;br&amp;gt;&lt;br /&gt;
Es wird empfohlen diesen Kurs mit dem Projekt-Modul [[GMU:eval(nature)|eval(nature)]] zu kombinieren.&lt;br /&gt;
&lt;br /&gt;
== Language ==&lt;br /&gt;
&lt;br /&gt;
The course will be in English, unless all participants are speaking German.&lt;br /&gt;
&lt;br /&gt;
== Eligible Participants ==&lt;br /&gt;
&lt;br /&gt;
Undergraduates and graduates enrolled in the faculties of:&lt;br /&gt;
&lt;br /&gt;
* Media Art/Design&lt;br /&gt;
* Visual Communication&lt;br /&gt;
&lt;br /&gt;
== Requirements ==&lt;br /&gt;
&lt;br /&gt;
* Burning Interest in Artificial Intelligence, Computer-Linguistics and Generative Design&lt;br /&gt;
* Previous course about the Internet of Things highly recommended&lt;br /&gt;
* Working knowledge of Web Technologies (HTML, CSS, Javascript ...)&lt;br /&gt;
* Time and Devotion for lots of coding homework, which may be frustrating at times&lt;br /&gt;
* Programming experience considered helpful&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
&lt;br /&gt;
The deadline to apply for this course is April 13th, 2015&lt;br /&gt;
&lt;br /&gt;
 You need to provide links to previous work to be elligible to this course.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;To:&#039;&#039;&#039; [[User:ms|Martin Schneider]]&amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Subject:&#039;&#039;&#039; Bots n Plots /// Application&amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Content:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# Please provide links to three of your visual, poetic or algorithmic works online.&lt;br /&gt;
# Please answer these questions:&lt;br /&gt;
#* What is your favourite bot on twitter?&lt;br /&gt;
#* What kind of bot would you like to be able to create at the end of the course?&lt;br /&gt;
# Please tell us about you:&lt;br /&gt;
#* program and semester (Studienprogramm und Fachsemester)&lt;br /&gt;
#* matriculation number (Matrikelnummer)&lt;br /&gt;
#* Valid email address @uni-weimar.de (no other mailing addresses will be accepted) [[SCC-Services#E-Mail|Why?]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
 This is the outline of the course.&lt;br /&gt;
 The details may still be subject to change.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Theme !! Topic !!  Date &lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | [[/Part1|Part 1: Python]]&lt;br /&gt;
| 01: Programming I&lt;br /&gt;
| 15. April 2015&lt;br /&gt;
|-&lt;br /&gt;
| 02: Programming II&lt;br /&gt;
| 22. April 2015&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;4&amp;quot; | [[/Part2|Part 2: Plotter Bots]]&lt;br /&gt;
| 03: Drawing and Plotting I&lt;br /&gt;
| 29. April 2015&lt;br /&gt;
|-&lt;br /&gt;
| 04: Drawing and Plotting II&lt;br /&gt;
| 06. May 2015&lt;br /&gt;
|-&lt;br /&gt;
| 05: Image Processing I&lt;br /&gt;
| 13. May 2015&lt;br /&gt;
|-&lt;br /&gt;
| 06: Image Processing II&lt;br /&gt;
| 20. May 2015&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;3&amp;quot; | [[/Part3|Part 3: Chatter Bots]]&lt;br /&gt;
| 07: Language Processing I&lt;br /&gt;
| 27. May 2015&lt;br /&gt;
|-&lt;br /&gt;
| 08: Language Processing II&lt;br /&gt;
| 03. June 2015&lt;br /&gt;
|-&lt;br /&gt;
| 09: Generating Poetry&lt;br /&gt;
| 10. June 2015&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;4&amp;quot; | [[/Part4|Part 4: Social Bots]]&lt;br /&gt;
| 10: Twitter Bots I&lt;br /&gt;
| 17. June 2015&lt;br /&gt;
|-&lt;br /&gt;
| 11: Twitter Bots II&lt;br /&gt;
| 24. June 2015&lt;br /&gt;
|-&lt;br /&gt;
| 12: Wiki Bots I&lt;br /&gt;
| 01. July 2015&lt;br /&gt;
|-&lt;br /&gt;
| 13: Wiki Bots II&lt;br /&gt;
| 08. July 2015&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Evaluation ==&lt;br /&gt;
&lt;br /&gt;
* Presence and active participation (required)&lt;br /&gt;
* Punctual and complete submission of homework (required)&lt;br /&gt;
&lt;br /&gt;
* 50% Technical and aesthetic execution of the final bot project&lt;br /&gt;
* 20% Emergent behaviour from making the robots talk&lt;br /&gt;
* 20% Documentation of the final project&lt;br /&gt;
* 10% Contribution to the MediaWiki&lt;br /&gt;
&lt;br /&gt;
== Participants ==&lt;br /&gt;
&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Sebastian Stang|Sebastian Stang]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Maria Degand|Maria Degand]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Klaus Kraemer|Klaus Kraemer]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Angelica Sohn|Angelica Sohn]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Luis Hurtarte|Luis Hurtarte]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Apasri Titatarn|Apasri Titatarn]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Jan Dropmann|Jan Dropmann]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Azucena Sanchez|Azucena Sanchez]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Natalia Martínez|Natalia Martínez]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/smin kim|Smin Kim]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/minsoo hwang|minsoo Hwang]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Christopher Marx|Christopher Marx]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Shubhra Bhatt|Shubhra Bhatt]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Constantin Oestreich|Constantin Oestreich]]&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Here are a couple of links that should give you an idea of where this course is heading ...&lt;br /&gt;
&lt;br /&gt;
=== Learning Python ===&lt;br /&gt;
* [http://www.codecademy.com/en/tracks/python Learn Python] at Code Academy&lt;br /&gt;
* [http://www.diveintopython3.net Dive Into Python 3]&lt;br /&gt;
&lt;br /&gt;
=== Image Processing ===&lt;br /&gt;
* [http://software-carpentry.org/v4/media/ Multimedia Programming] by Software Carpentry&lt;br /&gt;
=== Video Processing ===&lt;br /&gt;
* [https://zulko.github.io/moviepy/ MoviePy] by Zulko&lt;br /&gt;
* [https://zulko.github.io/blog/2014/09/20/vector-animations-with-python/ Vector Animations with Python]&lt;br /&gt;
=== Audio Processing ===&lt;br /&gt;
====IO====&lt;br /&gt;
* [http://people.csail.mit.edu/hubert/pyaudio/ PyAudio] PortAudio wrapper&lt;br /&gt;
* [https://github.com/thestk/rtaudio/tree/master/contrib/python/pyrtaudio PyRtAudio] RtAudio wrapper&lt;br /&gt;
=== Language Processing ===&lt;br /&gt;
* [http://h6o6.com/2013/03/using-python-and-the-nltk-to-find-haikus-in-the-public-twitter-stream/ Using Python and the NLTK to find Haikus in the public twitter stream]&lt;br /&gt;
* [http://dhconnelly.com/paip-python/docs/paip/eliza.html Eliza.py]&lt;br /&gt;
=== Twitter ===&lt;br /&gt;
* [https://zulko.github.io/blog/2014/07/26/a-tweets-controlled-python-script/|A Python Script controlled via Twitter]&lt;br /&gt;
=== Contribution by Students ===&lt;br /&gt;
* [http://www.belfasttelegraph.co.uk/technology/robot-that-bought-mdma-passport-and-baseball-cap-released-by-authorities-31150495.html &amp;quot;Random Darknet Shopper&amp;quot; bot released from prison]&lt;br /&gt;
&lt;br /&gt;
== Literature ==&lt;br /&gt;
* Bird, Steven: &#039;&#039;Natural Language Processing with Python&#039;&#039; ISBN 978-0596516499 ([http://www.nltk.org/book/ online])&lt;br /&gt;
* Lobin, Hennig:&#039;&#039;Computerlinguistik und Texttechnologie&#039;&#039; ISBN 978-3825232825 &lt;br /&gt;
* Russel, Mathew: &#039;&#039;Mining the Social Web&#039;&#039; ISBN 978-1449367619&lt;br /&gt;
* Segaran, Toby&#039;&#039;: Programming Collective Intelligence&#039;&#039; ISBN 978-0596529321&lt;br /&gt;
&lt;br /&gt;
=== Python ===&lt;br /&gt;
This is a list of recommended books for the Python language:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;!-- {{PythonBooks}} --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Beazley, David: &#039;&#039;Python Cookbook&#039;&#039; ISBN 978-1449340377 &lt;br /&gt;
* Blum, Richard: &#039;&#039;Python Programming for Raspberry Pi&#039;&#039; ISBN 978-0789752055 &lt;br /&gt;
* Chan, Jamie: &#039;&#039;Learn Python in One Day and Learn it Well&#039;&#039; ISBN 978-1506094380&lt;br /&gt;
* Zelle, John: &#039;&#039;Python Programming: An Introduction to Computer Science&#039;&#039; ISBN 978-1590282410 ([http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.111.6062 online])&lt;br /&gt;
&lt;br /&gt;
=== Further Reading ===&lt;br /&gt;
There are a couple of books in the Semesterapparat of [[Society of Networked Things]], that you might want to flip trough.&lt;br /&gt;
&lt;br /&gt;
[[Category:SS15]]&lt;br /&gt;
[[Category:Werkmodul]]&lt;br /&gt;
[[Category:Fachmodul]]&lt;br /&gt;
[[Category:Martin Schneider]]&lt;br /&gt;
[[Category:Python]]&lt;br /&gt;
[[Category:Gestaltung]]&lt;br /&gt;
[[Category:Design]]&lt;br /&gt;
[[Category:Poetry]]&lt;br /&gt;
[[Category:Generative Gestaltung]]&lt;br /&gt;
[[Category:Robots]]&lt;/div&gt;</summary>
		<author><name>SebastianStang</name></author>
	</entry>
	<entry>
		<id>https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=GMU:Bots_%27n%27_Plots&amp;diff=71515</id>
		<title>GMU:Bots &#039;n&#039; Plots</title>
		<link rel="alternate" type="text/html" href="https://www.uni-weimar.de/kunst-und-gestaltung/wiki/index.php?title=GMU:Bots_%27n%27_Plots&amp;diff=71515"/>
		<updated>2015-04-18T03:38:00Z</updated>

		<summary type="html">&lt;p&gt;SebastianStang: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Image:Bots-n-plots-maillardet-drawing.png|thumb|left|300px|&#039;&#039;Drawing by [[wikipedia:Maillardet&#039;s automaton|the Draughtsman-Writer Bot]] ca. 1800&#039;&#039;]] &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bots &#039;n&#039; Plots &amp;amp;mdash; Malen nach 0 und 1&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[:Category:Werkmodul|Werkmodul]]/[[:Category:Fachmodul|Fachmodul]]&amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;Lecturer:&#039;&#039; [[User:ms|Martin Schneider]]&amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;Credits:&#039;&#039; 6 [[ECTS]], 4 [[SWS]]&amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;Date:&#039;&#039; Wednesday 17:00 - 20:30 Uhr &amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;Venue:&#039;&#039; [[Marienstraße 7b]], Room 204&amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;First meeting:&#039;&#039; Wednesday, 15. Apr. 2015&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br style=&amp;quot;clear: both&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;NEWS&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;The Deadline has been extended by 24 hours :)&#039;&#039;&#039;&lt;br /&gt;
 &#039;&#039;&#039;You need to apply until 24:00 on April 13th, 2015.&#039;&#039;&#039;&lt;br /&gt;
 Applications after the deadline will not be taken into consideration.&lt;br /&gt;
 I will let you know if you made it onto the list on April 14th.&lt;br /&gt;
&lt;br /&gt;
 &#039;&#039;&#039;Follow [http://twitter.com/botsnplots @botsnplots] on Twitter!&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;The internet of things is infiltrating the social web.&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;A rich ecosystem of social bots is evolving right now.&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;Those bots range from the purposeful via the poetic to the philosophical.&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;And they are starting to talk to each other.&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the Bots &#039;n&#039; Plots course, we will create little robots that doodle and babble.  &amp;lt;br&amp;gt;&lt;br /&gt;
Our robots will be based in software, but we will also learn how to connect them with the physical world, using vision sound and serial connections.  &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the course of the class you will:&lt;br /&gt;
&lt;br /&gt;
* Learn programming with Python&lt;br /&gt;
* Process images and text&lt;br /&gt;
* Create generative drawings and poems&lt;br /&gt;
* Use APIs for the social web, including Twitter and Mediawiki&lt;br /&gt;
* Create your own software bots and make them talk to each other&lt;br /&gt;
&lt;br /&gt;
The course requires no previous programming experience.&lt;br /&gt;
&lt;br /&gt;
As a result of the course you will acquire fundamental programming skills that will enable you to take more advanced courses in the future. &amp;lt;br&amp;gt;&lt;br /&gt;
It is recommended to combine the course with the [[GMU:eval(nature)|eval(nature) module]].&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beschreibung ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Das Internet der Dinge erobert die sozialen Netzwerke.&#039;&#039;  &amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;Im Augenblick entwickelt sich ein vielfältiges Ökosystem sozialer Bots.&#039;&#039;  &amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;Die Bots decken ein weites Spektrum ab, vom Praktischen, über Poetisches bis hin zum Philosophischen.&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;Und sie fangen an miteinander zu kommunizieren...&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In &#039;&#039;Malen nach 0 und 1&#039;&#039; werden wir kleine Roboter erschaffen, die kritzeln und brabbeln. &amp;lt;br&amp;gt;&lt;br /&gt;
Unsere Roboter bestehen aus Nullen und Einsen, aber wir werden auch lernen sie mit der materiellen Welt zu verbinden, wobei Kameras, Töne und Kabel zum Einsatz kommen. &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Rahmen des Moduls werdet ihr:&lt;br /&gt;
&lt;br /&gt;
* Programmieren mit Python lernen&lt;br /&gt;
* Grafik und Text verarbeiten&lt;br /&gt;
* Generative Grafiken und Gedichte erzeugen&lt;br /&gt;
* APIs für Soziale Netzwerke nutzen, inkl. Twitter und Mediawiki&lt;br /&gt;
* Eigene Software-Bots entwickeln und sie dazu bringen miteinander zu reden.&lt;br /&gt;
&lt;br /&gt;
Dieses Modul erfordert keine Programmierkentnisse.&lt;br /&gt;
&lt;br /&gt;
Im Rahmen des Kurses werden grundlegende Programmierkentnisse vermittelt, die es ermöglichen in Zukunft fortgeschrittene Kurse zu belegen.&amp;lt;br&amp;gt;&lt;br /&gt;
Es wird empfohlen diesen Kurs mit dem Projekt-Modul [[GMU:eval(nature)|eval(nature)]] zu kombinieren.&lt;br /&gt;
&lt;br /&gt;
== Language ==&lt;br /&gt;
&lt;br /&gt;
The course will be in English, unless all participants are speaking German.&lt;br /&gt;
&lt;br /&gt;
== Eligible Participants ==&lt;br /&gt;
&lt;br /&gt;
Undergraduates and graduates enrolled in the faculties of:&lt;br /&gt;
&lt;br /&gt;
* Media Art/Design&lt;br /&gt;
* Visual Communication&lt;br /&gt;
&lt;br /&gt;
== Requirements ==&lt;br /&gt;
&lt;br /&gt;
* Burning Interest in Artificial Intelligence, Computer-Linguistics and Generative Design&lt;br /&gt;
* Previous course about the Internet of Things highly recommended&lt;br /&gt;
* Working knowledge of Web Technologies (HTML, CSS, Javascript ...)&lt;br /&gt;
* Time and Devotion for lots of coding homework, which may be frustrating at times&lt;br /&gt;
* Programming experience considered helpful&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
&lt;br /&gt;
The deadline to apply for this course is April 13th, 2015&lt;br /&gt;
&lt;br /&gt;
 You need to provide links to previous work to be elligible to this course.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;To:&#039;&#039;&#039; [[User:ms|Martin Schneider]]&amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Subject:&#039;&#039;&#039; Bots n Plots /// Application&amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Content:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# Please provide links to three of your visual, poetic or algorithmic works online.&lt;br /&gt;
# Please answer these questions:&lt;br /&gt;
#* What is your favourite bot on twitter?&lt;br /&gt;
#* What kind of bot would you like to be able to create at the end of the course?&lt;br /&gt;
# Please tell us about you:&lt;br /&gt;
#* program and semester (Studienprogramm und Fachsemester)&lt;br /&gt;
#* matriculation number (Matrikelnummer)&lt;br /&gt;
#* Valid email address @uni-weimar.de (no other mailing addresses will be accepted) [[SCC-Services#E-Mail|Why?]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
 This is the outline of the course.&lt;br /&gt;
 The details may still be subject to change.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Theme !! Topic !!  Date &lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | [[/Part1|Part 1: Python]]&lt;br /&gt;
| 01: Programming I&lt;br /&gt;
| 15. April 2015&lt;br /&gt;
|-&lt;br /&gt;
| 02: Programming II&lt;br /&gt;
| 22. April 2015&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;4&amp;quot; | [[/Part2|Part 2: Plotter Bots]]&lt;br /&gt;
| 03: Drawing and Plotting I&lt;br /&gt;
| 29. April 2015&lt;br /&gt;
|-&lt;br /&gt;
| 04: Drawing and Plotting II&lt;br /&gt;
| 06. May 2015&lt;br /&gt;
|-&lt;br /&gt;
| 05: Image Processing I&lt;br /&gt;
| 13. May 2015&lt;br /&gt;
|-&lt;br /&gt;
| 06: Image Processing II&lt;br /&gt;
| 20. May 2015&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;3&amp;quot; | [[/Part3|Part 3: Chatter Bots]]&lt;br /&gt;
| 07: Language Processing I&lt;br /&gt;
| 27. May 2015&lt;br /&gt;
|-&lt;br /&gt;
| 08: Language Processing II&lt;br /&gt;
| 03. June 2015&lt;br /&gt;
|-&lt;br /&gt;
| 09: Generating Poetry&lt;br /&gt;
| 10. June 2015&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;4&amp;quot; | [[/Part4|Part 4: Social Bots]]&lt;br /&gt;
| 10: Twitter Bots I&lt;br /&gt;
| 17. June 2015&lt;br /&gt;
|-&lt;br /&gt;
| 11: Twitter Bots II&lt;br /&gt;
| 24. June 2015&lt;br /&gt;
|-&lt;br /&gt;
| 12: Wiki Bots I&lt;br /&gt;
| 01. July 2015&lt;br /&gt;
|-&lt;br /&gt;
| 13: Wiki Bots II&lt;br /&gt;
| 08. July 2015&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Evaluation ==&lt;br /&gt;
&lt;br /&gt;
* Presence and active participation (required)&lt;br /&gt;
* Punctual and complete submission of homework (required)&lt;br /&gt;
&lt;br /&gt;
* 50% Technical and aesthetic execution of the final bot project&lt;br /&gt;
* 20% Emergent behaviour from making the robots talk&lt;br /&gt;
* 20% Documentation of the final project&lt;br /&gt;
* 10% Contribution to the MediaWiki&lt;br /&gt;
&lt;br /&gt;
== Participants ==&lt;br /&gt;
&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Sebastian Stang|Sebastian Stang]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Maria Degand|Maria Degand]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Klaus Kraemer|Klaus Kraemer]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Angelica Sohn|Angelica Sohn]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Luis Hurtarte|Luis Hurtarte]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Apasri Titatarn|Apasri Titatarn]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Jan Dropmann|Jan Dropmann]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Azucena Sanchez|Azucena Sanchez]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Natalia Martínez|Natalia Martínez]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/smin kim|Smin Kim]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/minsoo hwang|minsoo Hwang]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Christopher Marx|Christopher Marx]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Shubhra Bhatt|Shubhra Bhatt]]&lt;br /&gt;
* [[GMU:Bots_&#039;n&#039;_Plots/Constantin Oestreich|Constantin Oestreich]]&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
Here are a couple of links that should give you an idea of where this course is heading ...&lt;br /&gt;
&lt;br /&gt;
=== Learning Python ===&lt;br /&gt;
* [http://www.codecademy.com/en/tracks/python Learn Python] at Code Academy&lt;br /&gt;
* [http://www.diveintopython3.net Dive Into Python 3]&lt;br /&gt;
&lt;br /&gt;
=== Image Processing ===&lt;br /&gt;
* [http://software-carpentry.org/v4/media/ Multimedia Programming] by Software Carpentry&lt;br /&gt;
=== Video Processing ===&lt;br /&gt;
* [https://zulko.github.io/moviepy/ MoviePy] by Zulko&lt;br /&gt;
* [https://zulko.github.io/blog/2014/09/20/vector-animations-with-python/ Vector Animations with Python]&lt;br /&gt;
=== Language Processing ===&lt;br /&gt;
* [http://h6o6.com/2013/03/using-python-and-the-nltk-to-find-haikus-in-the-public-twitter-stream/ Using Python and the NLTK to find Haikus in the public twitter stream]&lt;br /&gt;
* [http://dhconnelly.com/paip-python/docs/paip/eliza.html Eliza.py]&lt;br /&gt;
=== Twitter ===&lt;br /&gt;
* [https://zulko.github.io/blog/2014/07/26/a-tweets-controlled-python-script/|A Python Script controlled via Twitter]&lt;br /&gt;
&lt;br /&gt;
== Literature ==&lt;br /&gt;
* Bird, Steven: &#039;&#039;Natural Language Processing with Python&#039;&#039; ISBN 978-0596516499 ([http://www.nltk.org/book/ online])&lt;br /&gt;
* Lobin, Hennig:&#039;&#039;Computerlinguistik und Texttechnologie&#039;&#039; ISBN 978-3825232825 &lt;br /&gt;
* Russel, Mathew: &#039;&#039;Mining the Social Web&#039;&#039; ISBN 978-1449367619&lt;br /&gt;
* Segaran, Toby&#039;&#039;: Programming Collective Intelligence&#039;&#039; ISBN 978-0596529321&lt;br /&gt;
&lt;br /&gt;
=== Python ===&lt;br /&gt;
This is a list of recommended books for the Python language:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;!-- {{PythonBooks}} --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Beazley, David: &#039;&#039;Python Cookbook&#039;&#039; ISBN 978-1449340377 &lt;br /&gt;
* Blum, Richard: &#039;&#039;Python Programming for Raspberry Pi&#039;&#039; ISBN 978-0789752055 &lt;br /&gt;
* Chan, Jamie: &#039;&#039;Learn Python in One Day and Learn it Well&#039;&#039; ISBN 978-1506094380&lt;br /&gt;
* Zelle, John: &#039;&#039;Python Programming: An Introduction to Computer Science&#039;&#039; ISBN 978-1590282410 ([http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.111.6062 online])&lt;br /&gt;
&lt;br /&gt;
=== Further Reading ===&lt;br /&gt;
There are a couple of books in the Semesterapparat of [[Society of Networked Things]], that you might want to flip trough.&lt;br /&gt;
&lt;br /&gt;
[[Category:SS15]]&lt;br /&gt;
[[Category:Werkmodul]]&lt;br /&gt;
[[Category:Fachmodul]]&lt;br /&gt;
[[Category:Martin Schneider]]&lt;br /&gt;
[[Category:Python]]&lt;br /&gt;
[[Category:Gestaltung]]&lt;br /&gt;
[[Category:Design]]&lt;br /&gt;
[[Category:Poetry]]&lt;br /&gt;
[[Category:Generative Gestaltung]]&lt;br /&gt;
[[Category:Robots]]&lt;/div&gt;</summary>
		<author><name>SebastianStang</name></author>
	</entry>
</feed>