From b183f3e0cb926adf6654f87da97e193a4e7d8086 Mon Sep 17 00:00:00 2001 From: Anastasis Germanidis Date: Fri, 20 Feb 2015 21:46:50 -0500 Subject: [PATCH 01/47] changing the image size of snaps re: #5 --- snapchat_bots/constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) mode change 100644 => 100755 snapchat_bots/constants.py diff --git a/snapchat_bots/constants.py b/snapchat_bots/constants.py old mode 100644 new mode 100755 index 75d48fa..800ac0f --- a/snapchat_bots/constants.py +++ b/snapchat_bots/constants.py @@ -1,7 +1,7 @@ DEFAULT_TIMEOUT = 15 DEFAULT_DURATION = 5 -SNAP_IMAGE_DIMENSIONS = (290, 600) +SNAP_IMAGE_DIMENSIONS = (1334, 750) MEDIA_TYPE_UNKNOWN = -1 MEDIA_TYPE_IMAGE = 0 From 11502adec9a7424bcaa9f6dc170a9da193180830 Mon Sep 17 00:00:00 2001 From: Anastasis Germanidis Date: Sat, 21 Feb 2015 12:35:57 -0500 Subject: [PATCH 02/47] changing setup.py dependency from PIL to Pillow --- snapchat_bots/__init__.py | 0 snapchat_bots/bot.py | 0 snapchat_bots/exceptions.py | 0 snapchat_bots/snap.py | 0 snapchat_bots/utils.py | 0 5 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 snapchat_bots/__init__.py mode change 100644 => 100755 snapchat_bots/bot.py mode change 100644 => 100755 snapchat_bots/exceptions.py mode change 100644 => 100755 snapchat_bots/snap.py mode change 100644 => 100755 snapchat_bots/utils.py diff --git a/snapchat_bots/__init__.py b/snapchat_bots/__init__.py old mode 100644 new mode 100755 diff --git a/snapchat_bots/bot.py b/snapchat_bots/bot.py old mode 100644 new mode 100755 diff --git a/snapchat_bots/exceptions.py b/snapchat_bots/exceptions.py old mode 100644 new mode 100755 diff --git a/snapchat_bots/snap.py b/snapchat_bots/snap.py old mode 100644 new mode 100755 diff --git a/snapchat_bots/utils.py b/snapchat_bots/utils.py old mode 100644 new mode 100755 From 91e851cedcf12892d1ec51a6386b17ef681ea001 Mon Sep 17 00:00:00 2001 From: Anastasis Germanidis Date: Sat, 21 Feb 2015 12:38:28 -0500 Subject: [PATCH 03/47] changing setup.py dependency from PIL to Pillow --- README.md | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 831ca2d..f10fedc 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # SnapchatBot: Python library for building bots that live on Snapchat Introducing SnapchatBot, an easy way to program Snapchat accounts to do anything you want. -SnapchatBot can be used to easily create image-based notification services, chatbots, search interfaces, +SnapchatBot can be used to create image-based notification services, chatbots, search interfaces, and any kind of intelligent agent that uses picture messages as its interaction mode. ## Bots Included diff --git a/setup.py b/setup.py index 3abc406..aaf0f99 100644 --- a/setup.py +++ b/setup.py @@ -17,7 +17,7 @@ install_requires=[ 'schedule>=0.3.1', 'requests>=2.5.1', - 'PIL>=1.1.7', + 'Pillow>=2.7.0', 'pysnap>=0.1.1' ], dependency_links = ['https://github.com/martinp/pysnap/tarball/master#egg=pysnap-0.1.1'], From e36d8f40b38b3f5d43e46f53ecf6a278db769a94 Mon Sep 17 00:00:00 2001 From: Nick Mooney Date: Sat, 21 Feb 2015 18:28:13 -0500 Subject: [PATCH 04/47] Added duration to send_snap() and post_story() --- snapchat_bots/bot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/snapchat_bots/bot.py b/snapchat_bots/bot.py index fe16e34..51d6e7d 100644 --- a/snapchat_bots/bot.py +++ b/snapchat_bots/bot.py @@ -92,7 +92,7 @@ def send_snap(self, recipients, snap): self.log("Sending snap %s to %s" % (snap.snap_id, recipients_str)) - self.client.send(snap.media_id, recipients_str) + self.client.send(snap.media_id, recipients_str, snap.duration) def post_story(self, snap): if not snap.uploaded: @@ -100,7 +100,7 @@ def post_story(self, snap): snap.upload(self) self.log("Posting snap as story") - self.client.send_to_story(snap.media_id, media_type=snap.media_type) + self.client.send_to_story(snap.media_id, snap.duration, snap.media_type) def add_friend(self, username): self.client.add_friend(username) From c92e7a37109cbf344dd8abd0c385f5268444d1da Mon Sep 17 00:00:00 2001 From: Ethan Blackburn Date: Sat, 21 Feb 2015 22:27:41 -0600 Subject: [PATCH 05/47] passing capture_snaps=True to get_snaps() will now save snaps in the current directory --- examples/reflectorbot.py | 3 +-- snapchat_bots/bot.py | 10 ++++++---- snapchat_bots/snap.py | 2 +- snapchat_bots/utils.py | 10 ++++++++++ 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/examples/reflectorbot.py b/examples/reflectorbot.py index 3778c99..7e70f17 100644 --- a/examples/reflectorbot.py +++ b/examples/reflectorbot.py @@ -1,7 +1,6 @@ from argparse import ArgumentParser from snapchat_bots import SnapchatBot - class ReflectorBot(SnapchatBot): def on_snap(self, sender, snap): self.send_snap([sender], snap) @@ -20,4 +19,4 @@ def on_friend_delete(self, friend): args = parser.parse_args() bot = ReflectorBot(args.username, args.password) - bot.listen(timeout=5) + bot.listen(timeout=60) diff --git a/snapchat_bots/bot.py b/snapchat_bots/bot.py index fe16e34..c32dbc9 100644 --- a/snapchat_bots/bot.py +++ b/snapchat_bots/bot.py @@ -2,6 +2,7 @@ from pysnap import Snapchat from snap import Snap from constants import DEFAULT_TIMEOUT +from utils import save_snap FORMAT = '[%(asctime)-15s] %(message)s' logging.basicConfig(format=FORMAT) @@ -28,11 +29,13 @@ def log(self, message, level=logging.DEBUG): logger.log(level, "[%s-%s] %s" % (self.__class__.__name__, self.bot_id, message)) @staticmethod - def process_snap(snap_obj, data): + def process_snap(snap_obj, data, capture_snap=False): media_type = snap_obj["media_type"] sender = snap_obj["sender"] snap_id = snap_obj['id'] duration = snap_obj['time'] + if capture_snap: + save_snap(data, sender, media_type) snap = Snap(data=data, snap_id=snap_id, media_type=media_type, @@ -47,7 +50,6 @@ def listen(self, timeout=DEFAULT_TIMEOUT): while True: self.log("Querying for new snaps...") snaps = self.get_snaps() - if hasattr(self, "on_snap"): for snap in snaps: self.on_snap(snap.sender, snap) @@ -111,7 +113,7 @@ def delete_friend(self, username): def block(self, username): self.client.block(username) - def get_snaps(self, mark_viewed=True): + def get_snaps(self, mark_viewed=True, capture_snaps=False): snaps = self.client.get_snaps() ret = [] @@ -124,7 +126,7 @@ def get_snaps(self, mark_viewed=True): if data is None: continue - snap = self.process_snap(snap_obj, data) + snap = self.process_snap(snap_obj, data, capture_snaps) if mark_viewed: self.mark_viewed(snap) diff --git a/snapchat_bots/snap.py b/snapchat_bots/snap.py index 651bb5f..129010e 100644 --- a/snapchat_bots/snap.py +++ b/snapchat_bots/snap.py @@ -2,7 +2,7 @@ from PIL import Image from StringIO import StringIO -from utils import guess_type, create_temporary_file, get_video_duration, resize_image, file_extension_for_type +from utils import guess_type, create_temporary_file, get_video_duration, resize_image, file_extension_for_type, save_snap from constants import MEDIA_TYPE_IMAGE, MEDIA_TYPE_VIDEO, DEFAULT_DURATION, SNAP_IMAGE_DIMENSIONS from exceptions import UnknownMediaType diff --git a/snapchat_bots/utils.py b/snapchat_bots/utils.py index b7f8c5a..11e492d 100644 --- a/snapchat_bots/utils.py +++ b/snapchat_bots/utils.py @@ -11,6 +11,16 @@ def file_extension_for_type(media_type): def create_temporary_file(suffix): return tempfile.NamedTemporaryFile(suffix=suffix, delete=False) +def save_snap(data, sender, suffix): + if not int(suffix): + extension = ".jpg" + else: + extension = ".mp4" + now = datetime.datetime.now() + filename = '%s-%s.%s.%s-%s:%s:%s%s' % (sender, now.month, now.day, now.year, now.hour, now.minute, now.second, extension) + with open(filename, 'wb') as f: + f.write(data) + def is_video_file(path): return mimetypes.guess_type(path)[0].startswith("video") From 599026cc38c0f2b625121534faeb3b03f23a5001 Mon Sep 17 00:00:00 2001 From: Ethan Blackburn Date: Sat, 21 Feb 2015 22:28:52 -0600 Subject: [PATCH 06/47] added example for saving snaps --- examples/capturebot.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 examples/capturebot.py diff --git a/examples/capturebot.py b/examples/capturebot.py new file mode 100644 index 0000000..6b52a2b --- /dev/null +++ b/examples/capturebot.py @@ -0,0 +1,28 @@ +from argparse import ArgumentParser +import time + +from snapchat_bots import SnapchatBot + + +class CaptureBot(SnapchatBot): + def on_snap(self, sender, snap): + self.send_snap([sender], snap) + + def listen(self, timeout=15): + while True: + self.log("Querying for new snaps...") + snaps = self.get_snaps(capture_snaps=True) + for snap in snaps: + self.on_snap(snap.sender, snap) + + time.sleep(timeout) + +if __name__ == '__main__': + parser = ArgumentParser("Capture Bot") + parser.add_argument('-u', '--username', required=True, type=str, help="Username of the account to run the bot on") + parser.add_argument('-p', '--password', required=True, type=str, help="Password of the account to run the bot on") + + args = parser.parse_args() + + bot = CaptureBot(args.username, args.password) + bot.listen(timeout=60) From 26970e0565ce9e8f6ae0b865bcdca90b9c0233da Mon Sep 17 00:00:00 2001 From: Ethan Blackburn Date: Sat, 21 Feb 2015 22:34:34 -0600 Subject: [PATCH 07/47] cleaned up branch --- examples/reflectorbot.py | 3 ++- snapchat_bots/bot.py | 1 + snapchat_bots/snap.py | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/reflectorbot.py b/examples/reflectorbot.py index 7e70f17..3778c99 100644 --- a/examples/reflectorbot.py +++ b/examples/reflectorbot.py @@ -1,6 +1,7 @@ from argparse import ArgumentParser from snapchat_bots import SnapchatBot + class ReflectorBot(SnapchatBot): def on_snap(self, sender, snap): self.send_snap([sender], snap) @@ -19,4 +20,4 @@ def on_friend_delete(self, friend): args = parser.parse_args() bot = ReflectorBot(args.username, args.password) - bot.listen(timeout=60) + bot.listen(timeout=5) diff --git a/snapchat_bots/bot.py b/snapchat_bots/bot.py index c32dbc9..4c721c8 100644 --- a/snapchat_bots/bot.py +++ b/snapchat_bots/bot.py @@ -50,6 +50,7 @@ def listen(self, timeout=DEFAULT_TIMEOUT): while True: self.log("Querying for new snaps...") snaps = self.get_snaps() + if hasattr(self, "on_snap"): for snap in snaps: self.on_snap(snap.sender, snap) diff --git a/snapchat_bots/snap.py b/snapchat_bots/snap.py index 129010e..651bb5f 100644 --- a/snapchat_bots/snap.py +++ b/snapchat_bots/snap.py @@ -2,7 +2,7 @@ from PIL import Image from StringIO import StringIO -from utils import guess_type, create_temporary_file, get_video_duration, resize_image, file_extension_for_type, save_snap +from utils import guess_type, create_temporary_file, get_video_duration, resize_image, file_extension_for_type from constants import MEDIA_TYPE_IMAGE, MEDIA_TYPE_VIDEO, DEFAULT_DURATION, SNAP_IMAGE_DIMENSIONS from exceptions import UnknownMediaType From 442b76f4129ddcf4d0d396e8257670446d74254b Mon Sep 17 00:00:00 2001 From: Ethan Blackburn Date: Sat, 21 Feb 2015 22:34:34 -0600 Subject: [PATCH 08/47] cleaned up branch --- examples/reflectorbot.py | 3 ++- snapchat_bots/bot.py | 1 + snapchat_bots/snap.py | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/reflectorbot.py b/examples/reflectorbot.py index 7e70f17..3778c99 100644 --- a/examples/reflectorbot.py +++ b/examples/reflectorbot.py @@ -1,6 +1,7 @@ from argparse import ArgumentParser from snapchat_bots import SnapchatBot + class ReflectorBot(SnapchatBot): def on_snap(self, sender, snap): self.send_snap([sender], snap) @@ -19,4 +20,4 @@ def on_friend_delete(self, friend): args = parser.parse_args() bot = ReflectorBot(args.username, args.password) - bot.listen(timeout=60) + bot.listen(timeout=5) diff --git a/snapchat_bots/bot.py b/snapchat_bots/bot.py index c32dbc9..4c721c8 100644 --- a/snapchat_bots/bot.py +++ b/snapchat_bots/bot.py @@ -50,6 +50,7 @@ def listen(self, timeout=DEFAULT_TIMEOUT): while True: self.log("Querying for new snaps...") snaps = self.get_snaps() + if hasattr(self, "on_snap"): for snap in snaps: self.on_snap(snap.sender, snap) diff --git a/snapchat_bots/snap.py b/snapchat_bots/snap.py index 129010e..651bb5f 100644 --- a/snapchat_bots/snap.py +++ b/snapchat_bots/snap.py @@ -2,7 +2,7 @@ from PIL import Image from StringIO import StringIO -from utils import guess_type, create_temporary_file, get_video_duration, resize_image, file_extension_for_type, save_snap +from utils import guess_type, create_temporary_file, get_video_duration, resize_image, file_extension_for_type from constants import MEDIA_TYPE_IMAGE, MEDIA_TYPE_VIDEO, DEFAULT_DURATION, SNAP_IMAGE_DIMENSIONS from exceptions import UnknownMediaType From 67687e16776f73aa810e35628254cc89994e74ef Mon Sep 17 00:00:00 2001 From: Ethan Blackburn Date: Sat, 21 Feb 2015 22:40:00 -0600 Subject: [PATCH 09/47] fixed imports --- examples/capturebot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/capturebot.py b/examples/capturebot.py index 6b52a2b..88451e0 100644 --- a/examples/capturebot.py +++ b/examples/capturebot.py @@ -1,5 +1,5 @@ -from argparse import ArgumentParser import time +from argparse import ArgumentParser from snapchat_bots import SnapchatBot From 4d10441273eed7770c002b51566b8a02be3152c7 Mon Sep 17 00:00:00 2001 From: Anastasis Germanidis Date: Sun, 22 Feb 2015 09:19:25 -0500 Subject: [PATCH 10/47] Fixing #12 --- snapchat_bots/snap.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/snapchat_bots/snap.py b/snapchat_bots/snap.py index 651bb5f..3213029 100644 --- a/snapchat_bots/snap.py +++ b/snapchat_bots/snap.py @@ -3,7 +3,7 @@ from StringIO import StringIO from utils import guess_type, create_temporary_file, get_video_duration, resize_image, file_extension_for_type -from constants import MEDIA_TYPE_IMAGE, MEDIA_TYPE_VIDEO, DEFAULT_DURATION, SNAP_IMAGE_DIMENSIONS +from constants import MEDIA_TYPE_IMAGE, MEDIA_TYPE_VIDEO, MEDIA_TYPE_VIDEO_WITHOUT_AUDIO, DEFAULT_DURATION, SNAP_IMAGE_DIMENSIONS from exceptions import UnknownMediaType class Snap(object): @@ -11,7 +11,7 @@ class Snap(object): def from_file(path, duration = None): media_type = guess_type(path) - if media_type is MEDIA_TYPE_VIDEO: + if media_type is MEDIA_TYPE_VIDEO or MEDIA_TYPE_VIDEO_WITHOUT_AUDIO: if duration is None: duration = get_video_duration(path) tmp = create_temporary_file(".snap.mp4") output_path = tmp.name @@ -61,7 +61,7 @@ def __init__(self, **opts): self.file = create_temporary_file(suffix) - if self.media_type is MEDIA_TYPE_VIDEO: + if self.media_type is MEDIA_TYPE_VIDEO or MEDIA_TYPE_VIDEO_WITHOUT_AUDIO: self.file.write(opts['data']) self.file.flush() From e8bf47b55c4808e0fee563975a87f97e703c3219 Mon Sep 17 00:00:00 2001 From: Anastasis Germanidis Date: Sun, 22 Feb 2015 09:31:43 -0500 Subject: [PATCH 11/47] fixed indentation issue --- snapchat_bots/snap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snapchat_bots/snap.py b/snapchat_bots/snap.py index 3213029..350dc78 100644 --- a/snapchat_bots/snap.py +++ b/snapchat_bots/snap.py @@ -61,7 +61,7 @@ def __init__(self, **opts): self.file = create_temporary_file(suffix) - if self.media_type is MEDIA_TYPE_VIDEO or MEDIA_TYPE_VIDEO_WITHOUT_AUDIO: + if self.media_type is MEDIA_TYPE_VIDEO or MEDIA_TYPE_VIDEO_WITHOUT_AUDIO: self.file.write(opts['data']) self.file.flush() From 86c8729a40fa7f9ec1d788c31ee1dde89c94d052 Mon Sep 17 00:00:00 2001 From: Ethan Blackburn Date: Sun, 22 Feb 2015 11:14:39 -0600 Subject: [PATCH 12/47] moved save snap functionality form get_snaps to utils.save_snap() --- examples/capturebot.py | 13 ++----------- snapchat_bots/bot.py | 8 +++----- snapchat_bots/utils.py | 13 ++++++------- 3 files changed, 11 insertions(+), 23 deletions(-) diff --git a/examples/capturebot.py b/examples/capturebot.py index 88451e0..294faa9 100644 --- a/examples/capturebot.py +++ b/examples/capturebot.py @@ -1,21 +1,12 @@ -import time from argparse import ArgumentParser - from snapchat_bots import SnapchatBot - +from snapchat_bots.utils import save_snap class CaptureBot(SnapchatBot): def on_snap(self, sender, snap): + save_snap(snap) self.send_snap([sender], snap) - def listen(self, timeout=15): - while True: - self.log("Querying for new snaps...") - snaps = self.get_snaps(capture_snaps=True) - for snap in snaps: - self.on_snap(snap.sender, snap) - - time.sleep(timeout) if __name__ == '__main__': parser = ArgumentParser("Capture Bot") diff --git a/snapchat_bots/bot.py b/snapchat_bots/bot.py index 4c721c8..2c752da 100644 --- a/snapchat_bots/bot.py +++ b/snapchat_bots/bot.py @@ -29,13 +29,11 @@ def log(self, message, level=logging.DEBUG): logger.log(level, "[%s-%s] %s" % (self.__class__.__name__, self.bot_id, message)) @staticmethod - def process_snap(snap_obj, data, capture_snap=False): + def process_snap(snap_obj, data): media_type = snap_obj["media_type"] sender = snap_obj["sender"] snap_id = snap_obj['id'] duration = snap_obj['time'] - if capture_snap: - save_snap(data, sender, media_type) snap = Snap(data=data, snap_id=snap_id, media_type=media_type, @@ -114,7 +112,7 @@ def delete_friend(self, username): def block(self, username): self.client.block(username) - def get_snaps(self, mark_viewed=True, capture_snaps=False): + def get_snaps(self, mark_viewed=True): snaps = self.client.get_snaps() ret = [] @@ -127,7 +125,7 @@ def get_snaps(self, mark_viewed=True, capture_snaps=False): if data is None: continue - snap = self.process_snap(snap_obj, data, capture_snaps) + snap = self.process_snap(snap_obj, data) if mark_viewed: self.mark_viewed(snap) diff --git a/snapchat_bots/utils.py b/snapchat_bots/utils.py index 11e492d..2626885 100644 --- a/snapchat_bots/utils.py +++ b/snapchat_bots/utils.py @@ -11,15 +11,14 @@ def file_extension_for_type(media_type): def create_temporary_file(suffix): return tempfile.NamedTemporaryFile(suffix=suffix, delete=False) -def save_snap(data, sender, suffix): - if not int(suffix): - extension = ".jpg" - else: - extension = ".mp4" +def save_snap(snap): now = datetime.datetime.now() - filename = '%s-%s.%s.%s-%s:%s:%s%s' % (sender, now.month, now.day, now.year, now.hour, now.minute, now.second, extension) + filename = '%s-%s.%s.%s-%s:%s:%s%s' % (snap.sender, now.month, now.day, now.year, now.hour, now.minute, now.second, snap.file.name[-4:]) with open(filename, 'wb') as f: - f.write(data) + data = snap.file.file.read(8192) + while data: + f.write(data) + data = snap.file.file.read(8192) def is_video_file(path): return mimetypes.guess_type(path)[0].startswith("video") From ad337f9a606bc769fc07df66f73d4a7f51bb5a7c Mon Sep 17 00:00:00 2001 From: Ethan Blackburn Date: Sun, 22 Feb 2015 11:22:49 -0600 Subject: [PATCH 13/47] removed old code --- examples/capturebot.py | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/capturebot.py b/examples/capturebot.py index 294faa9..a9ca25d 100644 --- a/examples/capturebot.py +++ b/examples/capturebot.py @@ -5,7 +5,6 @@ class CaptureBot(SnapchatBot): def on_snap(self, sender, snap): save_snap(snap) - self.send_snap([sender], snap) if __name__ == '__main__': From 3946b7ec50f75a489f2c871f3373952fa6d4a44e Mon Sep 17 00:00:00 2001 From: Anastasis Germanidis Date: Sun, 22 Feb 2015 12:55:27 -0500 Subject: [PATCH 14/47] Added Capture bot to Readme --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index f10fedc..3a3f212 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,11 @@ Posts popular GIFs taken from the [Giphy](http://giphy.com) home page to its sto When you add the Connector to your friends, it links you with a stranger who's also added it. Every snap sent to the Connector will then arrive at the stranger's inbox, and all snaps sent from the stranger to the Connector will come to you. It's like ChatRoulette on Snapchat. +### The Capture Bot (by [EthanBlackburn](https://github.com/EthanBlackburn)) +*(source at examples/capturebot.py)* + +Saves all snaps received to the current working directory. + ## Installation $ python setup.py install From 9817a36a542db2f0fc658f0114c66c82d912a60a Mon Sep 17 00:00:00 2001 From: Anastasis Germanidis Date: Sun, 22 Feb 2015 12:56:04 -0500 Subject: [PATCH 15/47] readme fix --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3a3f212..54b40aa 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ Posts popular GIFs taken from the [Giphy](http://giphy.com) home page to its sto When you add the Connector to your friends, it links you with a stranger who's also added it. Every snap sent to the Connector will then arrive at the stranger's inbox, and all snaps sent from the stranger to the Connector will come to you. It's like ChatRoulette on Snapchat. -### The Capture Bot (by [EthanBlackburn](https://github.com/EthanBlackburn)) +#### The Capture Bot (by [EthanBlackburn](https://github.com/EthanBlackburn)) *(source at examples/capturebot.py)* Saves all snaps received to the current working directory. From 52b25d0c1d186e5659dbc256809125a207e47b4d Mon Sep 17 00:00:00 2001 From: Ethan Blackburn Date: Sun, 22 Feb 2015 19:03:11 -0600 Subject: [PATCH 16/47] fixed error that prevented snaps from being saved --- snapchat_bots/snap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snapchat_bots/snap.py b/snapchat_bots/snap.py index 350dc78..7e967eb 100644 --- a/snapchat_bots/snap.py +++ b/snapchat_bots/snap.py @@ -61,7 +61,7 @@ def __init__(self, **opts): self.file = create_temporary_file(suffix) - if self.media_type is MEDIA_TYPE_VIDEO or MEDIA_TYPE_VIDEO_WITHOUT_AUDIO: + if self.media_type is MEDIA_TYPE_VIDEO or self.media_type is MEDIA_TYPE_VIDEO_WITHOUT_AUDIO: self.file.write(opts['data']) self.file.flush() From e21e52c66d32c6aa032acbd546307ab80a1794e1 Mon Sep 17 00:00:00 2001 From: Anastasis Germanidis Date: Mon, 23 Feb 2015 10:04:37 -0500 Subject: [PATCH 17/47] Update snap.py --- snapchat_bots/snap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snapchat_bots/snap.py b/snapchat_bots/snap.py index 7e967eb..ea6a089 100644 --- a/snapchat_bots/snap.py +++ b/snapchat_bots/snap.py @@ -11,7 +11,7 @@ class Snap(object): def from_file(path, duration = None): media_type = guess_type(path) - if media_type is MEDIA_TYPE_VIDEO or MEDIA_TYPE_VIDEO_WITHOUT_AUDIO: + if media_type is MEDIA_TYPE_VIDEO or media_type is MEDIA_TYPE_VIDEO_WITHOUT_AUDIO: if duration is None: duration = get_video_duration(path) tmp = create_temporary_file(".snap.mp4") output_path = tmp.name From 0853118dc307cd7eb01c507018f6667a7fb564e0 Mon Sep 17 00:00:00 2001 From: Anastasis Germanidis Date: Mon, 23 Feb 2015 11:23:28 -0500 Subject: [PATCH 18/47] adding quality parameter on im.save re: #5 --- snapchat_bots/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snapchat_bots/utils.py b/snapchat_bots/utils.py index 2626885..1425804 100755 --- a/snapchat_bots/utils.py +++ b/snapchat_bots/utils.py @@ -33,7 +33,7 @@ def guess_type(path): def resize_image(im, output_path): im.thumbnail(SNAP_IMAGE_DIMENSIONS, Image.ANTIALIAS) - im.save(output_path) + im.save(output_path, quality = 100) def duration_string_to_timedelta(s): [hours, minutes, seconds] = map(int, s.split(':')) From be337ae9e1c82a02f2ea71e795edad8646825468 Mon Sep 17 00:00:00 2001 From: N07070 Date: Tue, 24 Feb 2015 13:14:11 +0100 Subject: [PATCH 19/47] Update utils.py Added a way to save all the images in a snapbot_saves folder. It creates it if it doesn't exist, then comes back to the original folder. --- snapchat_bots/utils.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/snapchat_bots/utils.py b/snapchat_bots/utils.py index 1425804..c92bee7 100755 --- a/snapchat_bots/utils.py +++ b/snapchat_bots/utils.py @@ -1,4 +1,4 @@ -import tempfile, mimetypes, datetime, subprocess, re, math +import tempfile, mimetypes, datetime, subprocess, re, math, os from PIL import Image from constants import MEDIA_TYPE_IMAGE, MEDIA_TYPE_VIDEO, MEDIA_TYPE_VIDEO_WITHOUT_AUDIO, SNAP_IMAGE_DIMENSIONS @@ -13,12 +13,17 @@ def create_temporary_file(suffix): def save_snap(snap): now = datetime.datetime.now() + before_folder = os.getcwd() + if not os.path.exists("snapbot_saves"): + os.makedirs("snapbot_saves") + os.chdir("snapbot_saves") filename = '%s-%s.%s.%s-%s:%s:%s%s' % (snap.sender, now.month, now.day, now.year, now.hour, now.minute, now.second, snap.file.name[-4:]) with open(filename, 'wb') as f: data = snap.file.file.read(8192) while data: f.write(data) data = snap.file.file.read(8192) + os.chdir(before_folder) def is_video_file(path): return mimetypes.guess_type(path)[0].startswith("video") From 71112bc86291538020d0440923c552f07eb4c986 Mon Sep 17 00:00:00 2001 From: N07070 Date: Wed, 25 Feb 2015 03:50:16 +0100 Subject: [PATCH 20/47] Update utils.py Added an argument to the save_snap(), to be the name of the directory in which it will be saved. --- snapchat_bots/utils.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/snapchat_bots/utils.py b/snapchat_bots/utils.py index c92bee7..a172908 100755 --- a/snapchat_bots/utils.py +++ b/snapchat_bots/utils.py @@ -11,12 +11,12 @@ def file_extension_for_type(media_type): def create_temporary_file(suffix): return tempfile.NamedTemporaryFile(suffix=suffix, delete=False) -def save_snap(snap): +def save_snap(snap,dir_name): now = datetime.datetime.now() before_folder = os.getcwd() - if not os.path.exists("snapbot_saves"): - os.makedirs("snapbot_saves") - os.chdir("snapbot_saves") + if not os.path.exists(dir_name): + os.makedirs(dir_name) + os.chdir(dir_name) filename = '%s-%s.%s.%s-%s:%s:%s%s' % (snap.sender, now.month, now.day, now.year, now.hour, now.minute, now.second, snap.file.name[-4:]) with open(filename, 'wb') as f: data = snap.file.file.read(8192) From f29ff05a5596ce7da454a6a7fbdd0ba9c5369944 Mon Sep 17 00:00:00 2001 From: N07070 Date: Thu, 26 Feb 2015 01:58:03 +0100 Subject: [PATCH 21/47] Update utils.py Updated the name of the saved file to fit RFC 3339 ( http://www.faqs.org/rfcs/rfc3339.html ) --- snapchat_bots/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snapchat_bots/utils.py b/snapchat_bots/utils.py index a172908..6c7d870 100755 --- a/snapchat_bots/utils.py +++ b/snapchat_bots/utils.py @@ -17,7 +17,7 @@ def save_snap(snap,dir_name): if not os.path.exists(dir_name): os.makedirs(dir_name) os.chdir(dir_name) - filename = '%s-%s.%s.%s-%s:%s:%s%s' % (snap.sender, now.month, now.day, now.year, now.hour, now.minute, now.second, snap.file.name[-4:]) + filename = '%s-%s-%s_%s:%s:%s_%s%s' % (now.year, now.month, now.day, now.hour, now.minute, now.second, snap.sender, snap.file.name[-4:]) with open(filename, 'wb') as f: data = snap.file.file.read(8192) while data: From f73c6b80c5cb3aa086ba1c2495ac043fa18e94b8 Mon Sep 17 00:00:00 2001 From: Sam Gunaratne Date: Sat, 28 Feb 2015 01:08:52 +0000 Subject: [PATCH 22/47] Improved error messaging for login --- snapchat_bots/bot.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/snapchat_bots/bot.py b/snapchat_bots/bot.py index 43bbad0..93dd15a 100755 --- a/snapchat_bots/bot.py +++ b/snapchat_bots/bot.py @@ -17,7 +17,11 @@ def __init__(self, username, password, **kwargs): self.password = password self.client = Snapchat() - self.client.login(username, password) + result = self.client.login(username, password) + + if self.client.username is None and self.client.auth_token is None: + self.log('Authorization Failed, status: "%s" , message "%s"' % (result['status'], result['message'])) + raise SystemExit(1) self.current_friends = self.get_friends() self.added_me = self.get_added_me() From 8f286ca23a7ac842edf17a23f689363bf9faca65 Mon Sep 17 00:00:00 2001 From: Anastasis Germanidis Date: Mon, 2 Mar 2015 21:51:43 -0500 Subject: [PATCH 23/47] added delete_story method to SnapchatBot (#17) --- snapchat_bots/bot.py | 16 +++++++++++++++- snapchat_bots/snap.py | 1 + 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/snapchat_bots/bot.py b/snapchat_bots/bot.py index 43bbad0..400ba1a 100755 --- a/snapchat_bots/bot.py +++ b/snapchat_bots/bot.py @@ -101,7 +101,21 @@ def post_story(self, snap): snap.upload(self) self.log("Posting snap as story") - self.client.send_to_story(snap.media_id, snap.duration, snap.media_type) + response = self.client.send_to_story(snap.media_id, snap.duration, snap.media_type) + + try: + snap.story_id = response['json']['story']['id'] + except: + pass + + def delete_story(self, snap): + if snap.story_id is None: + return + + self.client._request('delete_story', { + 'username': self.username, + 'story_id': snap.story_id + }) def add_friend(self, username): self.client.add_friend(username) diff --git a/snapchat_bots/snap.py b/snapchat_bots/snap.py index ea6a089..ca260ba 100755 --- a/snapchat_bots/snap.py +++ b/snapchat_bots/snap.py @@ -44,6 +44,7 @@ def __init__(self, **opts): self.uploaded = False self.duration = opts['duration'] self.media_type = opts['media_type'] + self.story_id = None if 'sender' in opts: self.sender = opts['sender'] From 030892b286634c40821434650584ac66ef43332a Mon Sep 17 00:00:00 2001 From: Anastasis Germanidis Date: Tue, 3 Mar 2015 08:25:49 -0500 Subject: [PATCH 24/47] Update README.md mentioning libjpeg dependency --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 54b40aa..0d0b0cd 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ Saves all snaps received to the current working directory. $ python setup.py install -You also need to have [ffmpeg](https://www.ffmpeg.org/) and [ImageMagick](http://www.imagemagick.org/) installed. +You also need to have [ffmpeg](https://www.ffmpeg.org/), [ImageMagick](http://www.imagemagick.org/), and [libjpeg](http://libjpeg.sourceforge.net/) installed. ## How to build your own bots From 1713bd7b5266fdd31d984d02101a156061a7cdfa Mon Sep 17 00:00:00 2001 From: Anastasis Germanidis Date: Wed, 4 Mar 2015 01:15:52 -0500 Subject: [PATCH 25/47] login hotfix --- snapchat_bots/bot.py | 40 ++++++++++++++++++++++++++++++++++---- snapchat_bots/constants.py | 3 +++ 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/snapchat_bots/bot.py b/snapchat_bots/bot.py index 43bbad0..26aae3e 100644 --- a/snapchat_bots/bot.py +++ b/snapchat_bots/bot.py @@ -1,7 +1,7 @@ -import logging, time, uuid -from pysnap import Snapchat +import logging, time, uuid, requests +from pysnap import make_request_token, Snapchat from snap import Snap -from constants import DEFAULT_TIMEOUT +from constants import DEFAULT_TIMEOUT, STATIC_TOKEN, BASE_URL from utils import save_snap FORMAT = '[%(asctime)-15s] %(message)s' @@ -13,11 +13,22 @@ class SnapchatBot(object): def __init__(self, username, password, **kwargs): self.bot_id = uuid.uuid4().hex[0:4] + self.auth_token = STATIC_TOKEN + self.username = username self.password = password + r = self._make_request("/loq/login", { + 'username': self.username, + 'password': self.password + }) + + result = r.json() + self.auth_token = result['updates_response']['auth_token'] + self.client = Snapchat() - self.client.login(username, password) + self.client.username = username + self.client.auth_token = auth_token self.current_friends = self.get_friends() self.added_me = self.get_added_me() @@ -133,3 +144,24 @@ def get_snaps(self, mark_viewed=True): ret.append(snap) return ret + + def _make_request(self, path, data = None, method = 'POST', files = None): + if data is None: + data = {} + + headers = { + 'User-Agent': 'Snapchat/8.1.1 (iPhone5,1; iOS 8.1.3; gzip)', + 'Accept-Language': 'en-US;q=1, en;q=0.9', + 'Accept-Locale': 'en' + } + + now = timestamp() + + if method == 'POST': + data['timestamp'] = now + data['req_token'] = make_request_token(self.auth_token, str(now)) + resp = requests.post(BASE_URL + path, data = data, files = files, headers = headers) + else: + resp = requests.get(BASE_URL + path, params = data, headers = headers) + + return resp diff --git a/snapchat_bots/constants.py b/snapchat_bots/constants.py index 800ac0f..7727dba 100755 --- a/snapchat_bots/constants.py +++ b/snapchat_bots/constants.py @@ -1,3 +1,6 @@ +BASE_URL = 'https://feelinsonice-hrd.appspot.com' +STATIC_TOKEN = 'm198sOkJEn37DjqZ32lpRu76xmw288xSQ9' + DEFAULT_TIMEOUT = 15 DEFAULT_DURATION = 5 From 6f0d2eff8c25d0ef2b6efc750729519768878ca2 Mon Sep 17 00:00:00 2001 From: Anastasis Germanidis Date: Wed, 4 Mar 2015 01:19:29 -0500 Subject: [PATCH 26/47] login works again re: #26 --- snapchat_bots/bot.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/snapchat_bots/bot.py b/snapchat_bots/bot.py index bcce360..b7214dd 100755 --- a/snapchat_bots/bot.py +++ b/snapchat_bots/bot.py @@ -1,5 +1,6 @@ import logging, time, uuid, requests -from pysnap import make_request_token, Snapchat +from pysnap import Snapchat +from pysnap.utils import make_request_token, timestamp from snap import Snap from constants import DEFAULT_TIMEOUT, STATIC_TOKEN, BASE_URL from utils import save_snap @@ -28,7 +29,7 @@ def __init__(self, username, password, **kwargs): self.client = Snapchat() self.client.username = username - self.client.auth_token = auth_token + self.client.auth_token = self.auth_token self.current_friends = self.get_friends() self.added_me = self.get_added_me() From 4c8819c50edc5a38325548e75690e133af6f0aac Mon Sep 17 00:00:00 2001 From: Anastasis Germanidis Date: Wed, 4 Mar 2015 23:03:43 -0500 Subject: [PATCH 27/47] adding mascot --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0d0b0cd..0109f22 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -# SnapchatBot: Python library for building bots that live on Snapchat +![SnapchatBot](http://i.imgur.com/s8XADUn.png?1) + +# SnapchatBot: A library for making bots that live on Snapchat Introducing SnapchatBot, an easy way to program Snapchat accounts to do anything you want. SnapchatBot can be used to create image-based notification services, chatbots, search interfaces, From 48febfcae6ab2242e72773c4f74c2178d4e7c9e2 Mon Sep 17 00:00:00 2001 From: Anastasis Germanidis Date: Mon, 9 Mar 2015 00:19:35 -0400 Subject: [PATCH 28/47] making save a method of Snap instead of a utility function --- snapchat_bots/snap.py | 14 ++++++++++++-- snapchat_bots/utils.py | 15 +++------------ 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/snapchat_bots/snap.py b/snapchat_bots/snap.py index ca260ba..388fcd2 100755 --- a/snapchat_bots/snap.py +++ b/snapchat_bots/snap.py @@ -1,8 +1,8 @@ -import subprocess, uuid +import subprocess, uuid, os from PIL import Image from StringIO import StringIO -from utils import guess_type, create_temporary_file, get_video_duration, resize_image, file_extension_for_type +from utils import guess_type, create_temporary_file, get_video_duration, resize_image, file_extension_for_type, default_filename_for_snap from constants import MEDIA_TYPE_IMAGE, MEDIA_TYPE_VIDEO, MEDIA_TYPE_VIDEO_WITHOUT_AUDIO, DEFAULT_DURATION, SNAP_IMAGE_DIMENSIONS from exceptions import UnknownMediaType @@ -35,6 +35,16 @@ def from_image(img, duration=DEFAULT_DURATION): f = create_temporary_file(".jpg") resize_image(img, f.name) return Snap(path=f.name, media_type=MEDIA_TYPE_IMAGE, duration=duration) + + def save(self, output_filename = default_filename_for_snap(self), dir_name = "."): + if not os.path.exists(dir_name): + os.makedirs(dir_name) + + with open(os.path.join(dir_name, filename), 'wb') as f: + data = self.file.file.read(8192) + while data: + f.write(data) + data = self.file.file.read(8192) def upload(self, bot): self.media_id = bot.client.upload(self.file.name) diff --git a/snapchat_bots/utils.py b/snapchat_bots/utils.py index 6c7d870..a9ace5e 100755 --- a/snapchat_bots/utils.py +++ b/snapchat_bots/utils.py @@ -11,20 +11,11 @@ def file_extension_for_type(media_type): def create_temporary_file(suffix): return tempfile.NamedTemporaryFile(suffix=suffix, delete=False) -def save_snap(snap,dir_name): +def default_filename_for_snap(snap): now = datetime.datetime.now() - before_folder = os.getcwd() - if not os.path.exists(dir_name): - os.makedirs(dir_name) - os.chdir(dir_name) filename = '%s-%s-%s_%s:%s:%s_%s%s' % (now.year, now.month, now.day, now.hour, now.minute, now.second, snap.sender, snap.file.name[-4:]) - with open(filename, 'wb') as f: - data = snap.file.file.read(8192) - while data: - f.write(data) - data = snap.file.file.read(8192) - os.chdir(before_folder) - + return filename + def is_video_file(path): return mimetypes.guess_type(path)[0].startswith("video") From b2e2b11bc44d1c27fe49c7220ae68cee015a1604 Mon Sep 17 00:00:00 2001 From: Anastasis Germanidis Date: Mon, 9 Mar 2015 00:24:03 -0400 Subject: [PATCH 29/47] Added Snap#open method --- snapchat_bots/exceptions.py | 3 +++ snapchat_bots/snap.py | 12 +++++++++--- snapchat_bots/utils.py | 3 +++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/snapchat_bots/exceptions.py b/snapchat_bots/exceptions.py index f5acc7f..5f38aae 100755 --- a/snapchat_bots/exceptions.py +++ b/snapchat_bots/exceptions.py @@ -1,2 +1,5 @@ class UnknownMediaType(Exception): pass + +class CannotOpenFile(Exception): + pass diff --git a/snapchat_bots/snap.py b/snapchat_bots/snap.py index 388fcd2..a3f2ee0 100755 --- a/snapchat_bots/snap.py +++ b/snapchat_bots/snap.py @@ -2,9 +2,9 @@ from PIL import Image from StringIO import StringIO -from utils import guess_type, create_temporary_file, get_video_duration, resize_image, file_extension_for_type, default_filename_for_snap +from utils import guess_type, create_temporary_file, get_video_duration, resize_image, file_extension_for_type, default_filename_for_snap, cmd_exists from constants import MEDIA_TYPE_IMAGE, MEDIA_TYPE_VIDEO, MEDIA_TYPE_VIDEO_WITHOUT_AUDIO, DEFAULT_DURATION, SNAP_IMAGE_DIMENSIONS -from exceptions import UnknownMediaType +from exceptions import UnknownMediaType, CannotOpenFile class Snap(object): @staticmethod @@ -35,6 +35,12 @@ def from_image(img, duration=DEFAULT_DURATION): f = create_temporary_file(".jpg") resize_image(img, f.name) return Snap(path=f.name, media_type=MEDIA_TYPE_IMAGE, duration=duration) + + def open(self): + if not cmd_exists("open"): + raise CannotOpenFile("Cannot open file") + + subprocess.call("open %s" % self.file.name) def save(self, output_filename = default_filename_for_snap(self), dir_name = "."): if not os.path.exists(dir_name): @@ -68,7 +74,7 @@ def __init__(self, **opts): if 'data' in opts: self.media_type = opts['media_type'] - suffix = "." + file_extension_for_type(opts['media_type']) + suffix = file_extension_for_type(opts['media_type']) self.file = create_temporary_file(suffix) diff --git a/snapchat_bots/utils.py b/snapchat_bots/utils.py index a9ace5e..db9416c 100755 --- a/snapchat_bots/utils.py +++ b/snapchat_bots/utils.py @@ -2,6 +2,9 @@ from PIL import Image from constants import MEDIA_TYPE_IMAGE, MEDIA_TYPE_VIDEO, MEDIA_TYPE_VIDEO_WITHOUT_AUDIO, SNAP_IMAGE_DIMENSIONS +def cmd_exists(cmd): + return subprocess.call("type " + cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) == 0 + def file_extension_for_type(media_type): if media_type is MEDIA_TYPE_IMAGE: return ".jpg" From 1359a3e7adba839a751d3ddc94610fa053d36596 Mon Sep 17 00:00:00 2001 From: Anastasis Germanidis Date: Mon, 9 Mar 2015 00:31:18 -0400 Subject: [PATCH 30/47] Snap#save and Snap#open work properly --- examples/capturebot.py | 4 +--- snapchat_bots/bot.py | 1 - snapchat_bots/snap.py | 7 +++++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/capturebot.py b/examples/capturebot.py index a9ca25d..69d2c43 100644 --- a/examples/capturebot.py +++ b/examples/capturebot.py @@ -1,11 +1,9 @@ from argparse import ArgumentParser from snapchat_bots import SnapchatBot -from snapchat_bots.utils import save_snap class CaptureBot(SnapchatBot): def on_snap(self, sender, snap): - save_snap(snap) - + snap.save() if __name__ == '__main__': parser = ArgumentParser("Capture Bot") diff --git a/snapchat_bots/bot.py b/snapchat_bots/bot.py index b7214dd..fc50d3e 100755 --- a/snapchat_bots/bot.py +++ b/snapchat_bots/bot.py @@ -3,7 +3,6 @@ from pysnap.utils import make_request_token, timestamp from snap import Snap from constants import DEFAULT_TIMEOUT, STATIC_TOKEN, BASE_URL -from utils import save_snap FORMAT = '[%(asctime)-15s] %(message)s' logging.basicConfig(format=FORMAT) diff --git a/snapchat_bots/snap.py b/snapchat_bots/snap.py index a3f2ee0..bddbd8c 100755 --- a/snapchat_bots/snap.py +++ b/snapchat_bots/snap.py @@ -40,9 +40,12 @@ def open(self): if not cmd_exists("open"): raise CannotOpenFile("Cannot open file") - subprocess.call("open %s" % self.file.name) + subprocess.Popen(["open", self.file.name]) - def save(self, output_filename = default_filename_for_snap(self), dir_name = "."): + def save(self, output_filename = None, dir_name = "."): + if output_filename is None: + output_filename = default_filename_for_snap(self) + if not os.path.exists(dir_name): os.makedirs(dir_name) From 08da5700d6839b685e717e2d6391430fed209ddc Mon Sep 17 00:00:00 2001 From: Anastasis Germanidis Date: Mon, 9 Mar 2015 13:56:57 -0400 Subject: [PATCH 31/47] fix #31 --- snapchat_bots/snap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snapchat_bots/snap.py b/snapchat_bots/snap.py index bddbd8c..f003d42 100755 --- a/snapchat_bots/snap.py +++ b/snapchat_bots/snap.py @@ -49,7 +49,7 @@ def save(self, output_filename = None, dir_name = "."): if not os.path.exists(dir_name): os.makedirs(dir_name) - with open(os.path.join(dir_name, filename), 'wb') as f: + with open(os.path.join(dir_name, output_filename), 'wb') as f: data = self.file.file.read(8192) while data: f.write(data) From aa8a4b692715104f1bc1fb5c13cd62c5998a19ec Mon Sep 17 00:00:00 2001 From: PhlexPlexico Date: Mon, 9 Mar 2015 18:17:56 -0600 Subject: [PATCH 32/47] Added RandoBot Also update README to give description of RandoBot, and included a welcome splash image. --- README.md | 5 ++++ examples/randobot.py | 58 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 examples/randobot.py diff --git a/README.md b/README.md index 0109f22..3de4177 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,11 @@ When you add the Connector to your friends, it links you with a stranger who's a #### The Capture Bot (by [EthanBlackburn](https://github.com/EthanBlackburn)) *(source at examples/capturebot.py)* +#### The RandoBot (by [PhlexPlexico](https://github.com/Phlexplexico)) +*(source at examples/randobot.py)* + +When you add RandoBot to your friends, it throws you in a list of people who's also added it. When you send it a snap, it will send your snap to a person randomly in the list. Much like the application "Rando". + Saves all snaps received to the current working directory. ## Installation diff --git a/examples/randobot.py b/examples/randobot.py new file mode 100644 index 0000000..bc28ba0 --- /dev/null +++ b/examples/randobot.py @@ -0,0 +1,58 @@ +from argparse import ArgumentParser +from snapchat_bots import SnapchatBot, Snap +import random + +class RandoBot(SnapchatBot): + def initialize(self): + self.connections = self.get_friends() + #If your bot ever gets blocked, uncomment these lines. + #Of course, make sure you have your old users backed up + #to the users.txt file! + #with open('users.txt', 'w') as file: + # for item in self.connections: + # print>>file, item + #f = open('users.txt', 'r') + #for line in f: + # self.add_friend(line) + #print(line) + print(self.connections) + + def connect(self,user): + self.log("Added user: %s to the array!" % (user)) + self.connections.append(user) + + def on_friend_add(self,friend): + self.add_friend(friend) + self.connect(friend) + + def on_friend_delete(self,friend): + self.delete_friend(friend) + self.connections.discard(friend) + + def find_random_user(self,username): + if len(self.connections) <= 1: + return None + newuser = random.choice(self.connections) + while(newuser == username): + newuser = random.choice(self.connections) + return newuser + + def on_snap(self,sender,snap): + connection = self.find_random_user(sender) + if sender not in self.connections: + self.connect(sender) + if connection: + self.send_snap([connection],snap) + print("%s sent snap to %s" % (sender,[connection])) + else: + self.send_snap([sender], Snap.from_file("../resources/rando_welcome.png")) + +if __name__ == '__main__': + parser = ArgumentParser("RandoBot Bot") + parser.add_argument('-u', '--username', required=True, type=str, help="Username of the account to run the bot on") + parser.add_argument('-p', '--password', required=True, type=str, help="Password of the account to run the bot on") + + args = parser.parse_args() + + bot = RandoBot(args.username, args.password) + bot.listen(timeout=33) From c3bb58cb4937de53306865b8d1a1fbf6bafff885 Mon Sep 17 00:00:00 2001 From: PhlexPlexico Date: Mon, 9 Mar 2015 18:18:52 -0600 Subject: [PATCH 33/47] Forgot splash image. --- resources/rando_welcome.png | Bin 0 -> 5840 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 resources/rando_welcome.png diff --git a/resources/rando_welcome.png b/resources/rando_welcome.png new file mode 100644 index 0000000000000000000000000000000000000000..ae176d25c54697c31ef21cb53cdb7d52dca16d2c GIT binary patch literal 5840 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 Date: Tue, 10 Mar 2015 12:16:20 -0600 Subject: [PATCH 34/47] Changed .discard to .remove Also when the user sends a snap and RandoBot is not on the list, it will now send a snap saying it must be added to the friend list, so they're not improperly put into the list. Attempted to fix some indentation issues as well, as spacing by four wasn't working for some? Commented lines are for if/when your bot gets blocked. Remove the first three comments of code, connect to your old bot and let it copy users into lists. Then, comment out those lines and uncomment the rest, to re-add all your users! --- examples/randobot.py | 26 ++++++++++++++------------ resources/rando_addme.png | Bin 0 -> 7462 bytes 2 files changed, 14 insertions(+), 12 deletions(-) create mode 100644 resources/rando_addme.png diff --git a/examples/randobot.py b/examples/randobot.py index bc28ba0..b5a8e2c 100644 --- a/examples/randobot.py +++ b/examples/randobot.py @@ -7,14 +7,16 @@ def initialize(self): self.connections = self.get_friends() #If your bot ever gets blocked, uncomment these lines. #Of course, make sure you have your old users backed up - #to the users.txt file! + #to the users.txt file! So you must uncomment the first + #three lines, while logged into the blocked bot, then + #uncomment the rest to re-add all users from the old bot. #with open('users.txt', 'w') as file: - # for item in self.connections: - # print>>file, item + # for item in self.connections: + # print>>file, item #f = open('users.txt', 'r') #for line in f: - # self.add_friend(line) - #print(line) + # self.add_friend(line) + # print(line) print(self.connections) def connect(self,user): @@ -27,25 +29,25 @@ def on_friend_add(self,friend): def on_friend_delete(self,friend): self.delete_friend(friend) - self.connections.discard(friend) + self.connections.remove(friend) def find_random_user(self,username): if len(self.connections) <= 1: - return None + return None newuser = random.choice(self.connections) while(newuser == username): - newuser = random.choice(self.connections) + newuser = random.choice(self.connections) return newuser def on_snap(self,sender,snap): connection = self.find_random_user(sender) if sender not in self.connections: - self.connect(sender) + self.send_snap([sender], Snap.from_file("../resources/rando_addme.png")) if connection: - self.send_snap([connection],snap) - print("%s sent snap to %s" % (sender,[connection])) + self.send_snap([connection],snap) + print("%s sent snap to %s" % (sender,[connection])) else: - self.send_snap([sender], Snap.from_file("../resources/rando_welcome.png")) + self.send_snap([sender], Snap.from_file("../resources/rando_welcome.png")) if __name__ == '__main__': parser = ArgumentParser("RandoBot Bot") diff --git a/resources/rando_addme.png b/resources/rando_addme.png new file mode 100644 index 0000000000000000000000000000000000000000..7128bb88a4d3e9f248a584d5942397cf1408e8b1 GIT binary patch literal 7462 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 K^|EXC|N0L?*k8v0 literal 0 HcmV?d00001 From 241e2356fbe840b572b83d8c8ceaa61fd8ba67ab Mon Sep 17 00:00:00 2001 From: Jordan Facibene Date: Thu, 12 Mar 2015 18:18:26 -0400 Subject: [PATCH 35/47] Update utils.py replaced ":" with "-" in default_filename_for_snap()'s filename --- snapchat_bots/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snapchat_bots/utils.py b/snapchat_bots/utils.py index db9416c..6f5f192 100755 --- a/snapchat_bots/utils.py +++ b/snapchat_bots/utils.py @@ -16,7 +16,7 @@ def create_temporary_file(suffix): def default_filename_for_snap(snap): now = datetime.datetime.now() - filename = '%s-%s-%s_%s:%s:%s_%s%s' % (now.year, now.month, now.day, now.hour, now.minute, now.second, snap.sender, snap.file.name[-4:]) + filename = '%s-%s-%s_%s-%s-%s_%s%s' % (now.year, now.month, now.day, now.hour, now.minute, now.second, snap.sender, snap.file.name[-4:]) return filename def is_video_file(path): From 8748ce2fe8e2ddaa30ce55aba834c2b95f758eee Mon Sep 17 00:00:00 2001 From: Anastasis Germanidis Date: Thu, 12 Mar 2015 20:35:45 -0400 Subject: [PATCH 36/47] readme fixes --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 3de4177..4847402 100644 --- a/README.md +++ b/README.md @@ -50,9 +50,7 @@ When you add the Connector to your friends, it links you with a stranger who's a #### The RandoBot (by [PhlexPlexico](https://github.com/Phlexplexico)) *(source at examples/randobot.py)* -When you add RandoBot to your friends, it throws you in a list of people who's also added it. When you send it a snap, it will send your snap to a person randomly in the list. Much like the application "Rando". - -Saves all snaps received to the current working directory. +When you add RandoBot to your friends, it throws you in a list of people who've also added it. When you send it a snap, it will send your snap to a random person in the list. Similar to the [Rando](http://techcrunch.com/2014/03/22/rip-rando/) app. ## Installation From 3b3fbd820c6fbbfda3994b3c3e33e669185b6a6c Mon Sep 17 00:00:00 2001 From: Anastasis Germanidis Date: Thu, 12 Mar 2015 20:36:22 -0400 Subject: [PATCH 37/47] Brought back CaptureBot description --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 4847402..6fc2c60 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,8 @@ When you add the Connector to your friends, it links you with a stranger who's a #### The Capture Bot (by [EthanBlackburn](https://github.com/EthanBlackburn)) *(source at examples/capturebot.py)* +Saves all snaps received to the current working directory. + #### The RandoBot (by [PhlexPlexico](https://github.com/Phlexplexico)) *(source at examples/randobot.py)* From 56c50c70fada2e4ca98514b1c241781ff383d094 Mon Sep 17 00:00:00 2001 From: Anastasis Germanidis Date: Thu, 12 Mar 2015 21:24:43 -0400 Subject: [PATCH 38/47] Added get_my_stories, get_friend_stories, clear_stories methods --- README.md | 3 +++ snapchat_bots/bot.py | 52 ++++++++++++++++++++++++++++++++++++++----- snapchat_bots/snap.py | 4 +++- 3 files changed, 53 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 0109f22..2cdd9d4 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,9 @@ You also need to have [ffmpeg](https://www.ffmpeg.org/), [ImageMagick](http://ww * `SnapchatBot#delete_friend(username)` -- deletes user with username `username` from the bot's friends * `SnapchatBot#block(username)` -- blocks user with username `username` * `SnapchatBot#get_snaps(mark_viewed = True)` -- gets snaps in the bot's inbox that haven't been viewed yet (use `mark_viewed = False` as a keyword argument if you don't want the bot to mark every snap received as viewed) +* `SnapchatBot#get_my_stories()` -- gets all snaps in the bot's story +* `SnapchatBot#get_friend_stories()` -- gets all the stories of the bot's friends +* `SnapchatBot#clear_stories()` -- deletes all the bot's stories * `SnapchatBot#mark_viewed(snap)` -- marks `snap` as viewed * `SnapchatBot#get_friends()` -- gets the bot's friends * `SnapchatBot#get_added_me()` -- gets all users that have added the bot to their friends diff --git a/snapchat_bots/bot.py b/snapchat_bots/bot.py index fc50d3e..9891ee9 100755 --- a/snapchat_bots/bot.py +++ b/snapchat_bots/bot.py @@ -1,4 +1,4 @@ -import logging, time, uuid, requests +import logging, time, uuid, requests, base64 from pysnap import Snapchat from pysnap.utils import make_request_token, timestamp from snap import Snap @@ -40,7 +40,7 @@ def log(self, message, level=logging.DEBUG): logger.log(level, "[%s-%s] %s" % (self.__class__.__name__, self.bot_id, message)) @staticmethod - def process_snap(snap_obj, data): + def process_snap(snap_obj, data, is_story = False): media_type = snap_obj["media_type"] sender = snap_obj["sender"] snap_id = snap_obj['id'] @@ -49,7 +49,8 @@ def process_snap(snap_obj, data): snap_id=snap_id, media_type=media_type, duration=duration, - sender=sender) + sender=sender, + is_story=is_story) return snap def mark_viewed(self, snap): @@ -120,6 +121,7 @@ def post_story(self, snap): pass def delete_story(self, snap): + print snap.story_id if snap.story_id is None: return @@ -137,8 +139,7 @@ def delete_friend(self, username): def block(self, username): self.client.block(username) - def get_snaps(self, mark_viewed=True): - snaps = self.client.get_snaps() + def process_snaps(self, snaps, mark_viewed = True): ret = [] for snap_obj in snaps: @@ -159,6 +160,47 @@ def get_snaps(self, mark_viewed=True): return ret + def process_stories(self, stories): + ret = [] + for snap_obj in stories: + media_key = base64.b64decode(snap_obj['media_key']) + media_iv = base64.b64decode(snap_obj['media_iv']) + data = self.client.get_story_blob(snap_obj['media_id'], + media_key, + media_iv) + if data is None: + continue + snap_obj['sender'] = self.username + snap = self.process_snap(snap_obj, data, is_story = True) + ret.append(snap) + return ret + + def get_snaps(self, mark_viewed=True): + snaps = self.client.get_snaps() + return self.process_snaps(snaps) + + def get_my_stories(self): + response = self.client._request('stories', { + 'username': self.username + }) + stories = map(lambda s: s['story'], response.json()['my_stories']) + return self.process_stories(stories) + + def get_friend_stories(self): + response = self.client._request('stories', { + 'username': self.username + }) + ret = [] + stories_per_friend = map(lambda s: s['stories'], response.json()['friend_stories']) + for stories_obj in stories_per_friend: + stories = map(lambda so: so['story'], stories_obj) + ret.extend(self.process_stories(stories)) + return ret + + def clear_stories(self): + for story in self.get_my_stories(): + self.delete_story(story) + def _make_request(self, path, data = None, method = 'POST', files = None): if data is None: data = {} diff --git a/snapchat_bots/snap.py b/snapchat_bots/snap.py index bddbd8c..4d7701e 100755 --- a/snapchat_bots/snap.py +++ b/snapchat_bots/snap.py @@ -63,7 +63,9 @@ def __init__(self, **opts): self.uploaded = False self.duration = opts['duration'] self.media_type = opts['media_type'] - self.story_id = None + + if opts.get("is_story", False): + self.story_id = opts['snap_id'] if 'sender' in opts: self.sender = opts['sender'] From bd014f7a2b25730899e5c76126d11c3fdf483d97 Mon Sep 17 00:00:00 2001 From: Anastasis Germanidis Date: Thu, 12 Mar 2015 21:55:15 -0400 Subject: [PATCH 39/47] Removing unused method --- README.md | 15 ++++++++------- snapchat_bots/bot.py | 17 +---------------- 2 files changed, 9 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 6e585a9..962033d 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,13 @@ Sends back everything you send it. *(source at examples/storifierbot.py)* Takes all the snaps sent to it and adds them to its story. It can be used to collect responses -from multiple people around a single theme, much like a Twitter hashtag. +from multiple people around a single theme, much like a Twitter +hashtag. + +#### The Capture Bot (by [EthanBlackburn](https://github.com/EthanBlackburn)) +*(source at examples/capturebot.py)* + +Saves all snaps received to the current working directory. #### The Auto-Welcomer Bot *(source at examples/autowelcomebot.py)* @@ -44,12 +50,7 @@ Posts popular GIFs taken from the [Giphy](http://giphy.com) home page to its sto When you add the Connector to your friends, it links you with a stranger who's also added it. Every snap sent to the Connector will then arrive at the stranger's inbox, and all snaps sent from the stranger to the Connector will come to you. It's like ChatRoulette on Snapchat. -#### The Capture Bot (by [EthanBlackburn](https://github.com/EthanBlackburn)) -*(source at examples/capturebot.py)* - -Saves all snaps received to the current working directory. - -#### The RandoBot (by [PhlexPlexico](https://github.com/Phlexplexico)) +#### The Rando Bot (by [PhlexPlexico](https://github.com/Phlexplexico)) *(source at examples/randobot.py)* When you add RandoBot to your friends, it throws you in a list of people who've also added it. When you send it a snap, it will send your snap to a random person in the list. Similar to the [Rando](http://techcrunch.com/2014/03/22/rip-rando/) app. diff --git a/snapchat_bots/bot.py b/snapchat_bots/bot.py index 9891ee9..f4d5299 100755 --- a/snapchat_bots/bot.py +++ b/snapchat_bots/bot.py @@ -160,22 +160,7 @@ def process_snaps(self, snaps, mark_viewed = True): return ret - def process_stories(self, stories): - ret = [] - for snap_obj in stories: - media_key = base64.b64decode(snap_obj['media_key']) - media_iv = base64.b64decode(snap_obj['media_iv']) - data = self.client.get_story_blob(snap_obj['media_id'], - media_key, - media_iv) - if data is None: - continue - snap_obj['sender'] = self.username - snap = self.process_snap(snap_obj, data, is_story = True) - ret.append(snap) - return ret - - def get_snaps(self, mark_viewed=True): + def get_snaps(self, mark_viewed=True): snaps = self.client.get_snaps() return self.process_snaps(snaps) From cb9c0783b68583e542ef7da71c39b4285873393a Mon Sep 17 00:00:00 2001 From: Anastasis Germanidis Date: Fri, 13 Mar 2015 12:04:38 -0400 Subject: [PATCH 40/47] bringing back process_stories() -- oops --- snapchat_bots/bot.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/snapchat_bots/bot.py b/snapchat_bots/bot.py index f4d5299..6607825 100755 --- a/snapchat_bots/bot.py +++ b/snapchat_bots/bot.py @@ -160,6 +160,21 @@ def process_snaps(self, snaps, mark_viewed = True): return ret + def process_stories(self, stories): + ret = [] + for snap_obj in stories: + media_key = base64.b64decode(snap_obj['media_key']) + media_iv = base64.b64decode(snap_obj['media_iv']) + data = self.client.get_story_blob(snap_obj['media_id'], + media_key, + media_iv) + if data is None: + continue + snap_obj['sender'] = self.username + snap = self.process_snap(snap_obj, data, is_story = True) + ret.append(snap) + return ret + def get_snaps(self, mark_viewed=True): snaps = self.client.get_snaps() return self.process_snaps(snaps) From d081a75b1d11ff99b6c9fd4cad8e5b919cd7b4ee Mon Sep 17 00:00:00 2001 From: N07070 Date: Sat, 14 Mar 2015 13:01:31 +0100 Subject: [PATCH 41/47] Update bot.py Fixed indention error. --- snapchat_bots/bot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snapchat_bots/bot.py b/snapchat_bots/bot.py index 6607825..9891ee9 100755 --- a/snapchat_bots/bot.py +++ b/snapchat_bots/bot.py @@ -175,7 +175,7 @@ def process_stories(self, stories): ret.append(snap) return ret - def get_snaps(self, mark_viewed=True): + def get_snaps(self, mark_viewed=True): snaps = self.client.get_snaps() return self.process_snaps(snaps) From 703739ec7281ef5d8501a11b6fd40b1b01cef89d Mon Sep 17 00:00:00 2001 From: Anastasis Germanidis Date: Mon, 16 Mar 2015 00:58:38 -0400 Subject: [PATCH 42/47] adds support for zipped videos #18 --- snapchat_bots/bot.py | 25 ++++++++++++------------- snapchat_bots/snap.py | 26 ++++++++++++++++---------- snapchat_bots/utils.py | 18 ++++++++++++++++++ 3 files changed, 46 insertions(+), 23 deletions(-) diff --git a/snapchat_bots/bot.py b/snapchat_bots/bot.py index f4d5299..d199738 100755 --- a/snapchat_bots/bot.py +++ b/snapchat_bots/bot.py @@ -92,11 +92,7 @@ def get_added_me(self): return map(lambda fr: fr['name'], updates["added_friends"]) def send_snap(self, recipients, snap): - self.log("Preparing to send snap %s" % snap.snap_id) - - if not snap.uploaded: - self.log("Uploading snap %s" % snap.snap_id) - snap.upload(self) + media_id = self._upload_snap(snap) if type(recipients) is not list: recipients = [recipients] @@ -105,15 +101,11 @@ def send_snap(self, recipients, snap): self.log("Sending snap %s to %s" % (snap.snap_id, recipients_str)) - self.client.send(snap.media_id, recipients_str, snap.duration) + self.client.send(media_id, recipients_str, snap.duration) def post_story(self, snap): - if not snap.uploaded: - self.log("Uploading snap") - snap.upload(self) - - self.log("Posting snap as story") - response = self.client.send_to_story(snap.media_id, snap.duration, snap.media_type) + media_id = self._upload_snap(snap) + response = self.client.send_to_story(media_id, snap.duration, snap.media_type) try: snap.story_id = response['json']['story']['id'] @@ -160,7 +152,7 @@ def process_snaps(self, snaps, mark_viewed = True): return ret - def get_snaps(self, mark_viewed=True): + def get_snaps(self, mark_viewed=True): snaps = self.client.get_snaps() return self.process_snaps(snaps) @@ -186,6 +178,13 @@ def clear_stories(self): for story in self.get_my_stories(): self.delete_story(story) + def _upload_snap(self, snap): + if not snap.get("uploaded"): + snap.media_id = self.client.upload(snap.file.name) + snap.uploaded = True + + return snap.media_id + def _make_request(self, path, data = None, method = 'POST', files = None): if data is None: data = {} diff --git a/snapchat_bots/snap.py b/snapchat_bots/snap.py index 284e1dd..df70511 100755 --- a/snapchat_bots/snap.py +++ b/snapchat_bots/snap.py @@ -2,7 +2,7 @@ from PIL import Image from StringIO import StringIO -from utils import guess_type, create_temporary_file, get_video_duration, resize_image, file_extension_for_type, default_filename_for_snap, cmd_exists +from utils import guess_type, create_temporary_file, get_video_duration, resize_image, file_extension_for_type, default_filename_for_snap, cmd_exists, extract_zip_components from constants import MEDIA_TYPE_IMAGE, MEDIA_TYPE_VIDEO, MEDIA_TYPE_VIDEO_WITHOUT_AUDIO, DEFAULT_DURATION, SNAP_IMAGE_DIMENSIONS from exceptions import UnknownMediaType, CannotOpenFile @@ -36,6 +36,12 @@ def from_image(img, duration=DEFAULT_DURATION): resize_image(img, f.name) return Snap(path=f.name, media_type=MEDIA_TYPE_IMAGE, duration=duration) + def is_image(self): + return media_type is MEDIA_TYPE_IMAGE + + def is_video(self): + return media_type is MEDIA_TYPE_VIDEO or media_type is MEDIA_TYPE_VIDEO_WITHOUT_AUDIO + def open(self): if not cmd_exists("open"): raise CannotOpenFile("Cannot open file") @@ -55,10 +61,6 @@ def save(self, output_filename = None, dir_name = "."): f.write(data) data = self.file.file.read(8192) - def upload(self, bot): - self.media_id = bot.client.upload(self.file.name) - self.uploaded = True - def __init__(self, **opts): self.uploaded = False self.duration = opts['duration'] @@ -77,18 +79,22 @@ def __init__(self, **opts): self.from_me = True if 'data' in opts: - self.media_type = opts['media_type'] + data = opts['data'] - suffix = file_extension_for_type(opts['media_type']) + if data[0:2] == 'PK': + video_filename, _ = extract_zip_components(data) + self.file = open(video_filename, 'rb') - self.file = create_temporary_file(suffix) + else: + suffix = file_extension_for_type(opts['media_type']) + self.file = create_temporary_file(suffix) if self.media_type is MEDIA_TYPE_VIDEO or self.media_type is MEDIA_TYPE_VIDEO_WITHOUT_AUDIO: - self.file.write(opts['data']) + self.file.write(data) self.file.flush() else: - image = Image.open(StringIO(opts['data'])) + image = Image.open(StringIO(data)) resize_image(image, self.file.name) else: diff --git a/snapchat_bots/utils.py b/snapchat_bots/utils.py index 6f5f192..78cc462 100755 --- a/snapchat_bots/utils.py +++ b/snapchat_bots/utils.py @@ -1,6 +1,7 @@ import tempfile, mimetypes, datetime, subprocess, re, math, os from PIL import Image from constants import MEDIA_TYPE_IMAGE, MEDIA_TYPE_VIDEO, MEDIA_TYPE_VIDEO_WITHOUT_AUDIO, SNAP_IMAGE_DIMENSIONS +from zipfile import ZipFile def cmd_exists(cmd): return subprocess.call("type " + cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) == 0 @@ -34,6 +35,22 @@ def resize_image(im, output_path): im.thumbnail(SNAP_IMAGE_DIMENSIONS, Image.ANTIALIAS) im.save(output_path, quality = 100) +def extract_zip_components(data): + tmp = create_temporary_file(".zip") + tmp.write(data) + tmp.flush() + zipped_snap = ZipFile(tmp.name) + unzip_dir = os.path.join(tmp.name.split(".")[0]) + os.mkdir(unzip_dir) + zipped_snap.extractall(unzip_dir) + filenames = os.listdir(unzip_dir) + for filename in filenames: + if filename.startswith("media"): + video_component = os.path.join(unzip_dir, filename) + elif filename.startswith("overlay"): + overlay_component = os.path.join(unzip_dir, filename) + return video_component, overlay_component + def duration_string_to_timedelta(s): [hours, minutes, seconds] = map(int, s.split(':')) seconds = seconds + minutes * 60 + hours * 3600 @@ -44,3 +61,4 @@ def get_video_duration(path): matches = [x for x in result.stdout.readlines() if "Duration" in x] duration_string = re.findall(r'Duration: ([0-9:]*)', matches[0])[0] return math.ceil(duration_string_to_timedelta(duration_string).seconds) + From f9b316ac6de0189c716e97df825868beaa203159 Mon Sep 17 00:00:00 2001 From: Anastasis Germanidis Date: Mon, 16 Mar 2015 01:04:17 -0400 Subject: [PATCH 43/47] adding mp4 extension so pysnap won't complain #18 --- snapchat_bots/utils.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/snapchat_bots/utils.py b/snapchat_bots/utils.py index 78cc462..5efb764 100755 --- a/snapchat_bots/utils.py +++ b/snapchat_bots/utils.py @@ -46,10 +46,14 @@ def extract_zip_components(data): filenames = os.listdir(unzip_dir) for filename in filenames: if filename.startswith("media"): - video_component = os.path.join(unzip_dir, filename) + old_video_path = os.path.join(unzip_dir, filename) + new_video_path = os.path.join(unzip_dir, "video.mp4") + os.rename(old_video_path, new_video_path) + elif filename.startswith("overlay"): overlay_component = os.path.join(unzip_dir, filename) - return video_component, overlay_component + + return new_video_path, overlay_component def duration_string_to_timedelta(s): [hours, minutes, seconds] = map(int, s.split(':')) From c5e1b8f34c740f52117098cf5bc8f42e4c88a931 Mon Sep 17 00:00:00 2001 From: Anastasis Germanidis Date: Mon, 16 Mar 2015 20:43:46 -0400 Subject: [PATCH 44/47] fix for #45 --- snapchat_bots/bot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snapchat_bots/bot.py b/snapchat_bots/bot.py index 736c24a..3fdb1f7 100755 --- a/snapchat_bots/bot.py +++ b/snapchat_bots/bot.py @@ -194,7 +194,7 @@ def clear_stories(self): self.delete_story(story) def _upload_snap(self, snap): - if not snap.get("uploaded"): + if not snap.uploaded: snap.media_id = self.client.upload(snap.file.name) snap.uploaded = True From 8783fcfc385003c2b320fe659202fca225824223 Mon Sep 17 00:00:00 2001 From: Anastasis Germanidis Date: Sun, 22 Mar 2015 22:35:46 -0400 Subject: [PATCH 45/47] Fixing #53 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index aaf0f99..06780bb 100644 --- a/setup.py +++ b/setup.py @@ -16,7 +16,7 @@ packages=['snapchat_bots'], install_requires=[ 'schedule>=0.3.1', - 'requests>=2.5.1', + 'requests>=2.5.3', 'Pillow>=2.7.0', 'pysnap>=0.1.1' ], From e2171cd8b2dcbdd9e3b5c9fdaf2f02a94b3450fb Mon Sep 17 00:00:00 2001 From: Kristian Rekstad Date: Wed, 25 Mar 2015 16:21:04 +0100 Subject: [PATCH 46/47] Potential fix for #54 --- snapchat_bots/snap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snapchat_bots/snap.py b/snapchat_bots/snap.py index df70511..03f58b3 100755 --- a/snapchat_bots/snap.py +++ b/snapchat_bots/snap.py @@ -83,7 +83,7 @@ def __init__(self, **opts): if data[0:2] == 'PK': video_filename, _ = extract_zip_components(data) - self.file = open(video_filename, 'rb') + self.file = open(video_filename, 'rb+') else: suffix = file_extension_for_type(opts['media_type']) From 862caf2cbc7df22b7abd60289f522dd8518516af Mon Sep 17 00:00:00 2001 From: Anastasis Germanidis Date: Fri, 27 Oct 2017 11:30:20 -0400 Subject: [PATCH 47/47] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 962033d..90192ed 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +

This repo is deprecated due to changes in Snapchat's unofficial API.

+ ![SnapchatBot](http://i.imgur.com/s8XADUn.png?1) # SnapchatBot: A library for making bots that live on Snapchat