Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Modules and changes to accomodating sharing - which works

  • Loading branch information...
commit ee153efcb7f3516238229b846a535eb45d38e168 1 parent ee47da4
mpesce authored
326 contacts_example.py
... ... @@ -0,0 +1,326 @@
  1 +#!/usr/bin/python
  2 +#
  3 +# Copyright (C) 2008 Google Inc.
  4 +#
  5 +# Licensed under the Apache License, Version 2.0 (the "License");
  6 +# you may not use this file except in compliance with the License.
  7 +# You may obtain a copy of the License at
  8 +#
  9 +# http://www.apache.org/licenses/LICENSE-2.0
  10 +#
  11 +# Unless required by applicable law or agreed to in writing, software
  12 +# distributed under the License is distributed on an "AS IS" BASIS,
  13 +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 +# See the License for the specific language governing permissions and
  15 +# limitations under the License.
  16 +
  17 +
  18 +__author__ = 'api.jscudder (Jeffrey Scudder)'
  19 +
  20 +
  21 +import sys
  22 +import getopt
  23 +import getpass
  24 +import atom
  25 +import gdata.contacts
  26 +import gdata.contacts.service
  27 +
  28 +
  29 +class ContactsSample(object):
  30 + """ContactsSample object demonstrates operations with the Contacts feed."""
  31 +
  32 + def __init__(self, email, password):
  33 + """Constructor for the ContactsSample object.
  34 +
  35 + Takes an email and password corresponding to a gmail account to
  36 + demonstrate the functionality of the Contacts feed.
  37 +
  38 + Args:
  39 + email: [string] The e-mail address of the account to use for the sample.
  40 + password: [string] The password corresponding to the account specified by
  41 + the email parameter.
  42 +
  43 + Yields:
  44 + A ContactsSample object used to run the sample demonstrating the
  45 + functionality of the Contacts feed.
  46 + """
  47 + self.gd_client = gdata.contacts.service.ContactsService()
  48 + self.gd_client.email = email
  49 + self.gd_client.password = password
  50 + self.gd_client.source = 'GoogleInc-ContactsPythonSample-1'
  51 + self.gd_client.ProgrammaticLogin()
  52 +
  53 + def PrintFeed(self, feed, ctr=0):
  54 + """Prints out the contents of a feed to the console.
  55 +
  56 + Args:
  57 + feed: A gdata.contacts.ContactsFeed instance.
  58 + ctr: [int] The number of entries in this feed previously printed. This
  59 + allows continuous entry numbers when paging through a feed.
  60 +
  61 + Returns:
  62 + The number of entries printed, including those previously printed as
  63 + specified in ctr. This is for passing as an argument to ctr on
  64 + successive calls to this method.
  65 +
  66 + """
  67 + if not feed.entry:
  68 + print '\nNo entries in feed.\n'
  69 + return 0
  70 + for i, entry in enumerate(feed.entry):
  71 + print '\n%s %s' % (ctr+i+1, entry.title.text)
  72 + if entry.content:
  73 + print ' %s' % (entry.content.text)
  74 + for email in entry.email:
  75 + if email.primary and email.primary == 'true':
  76 + print ' %s' % (email.address)
  77 + # Show the contact groups that this contact is a member of.
  78 + for group in entry.group_membership_info:
  79 + print ' Member of group: %s' % (group.href)
  80 + # Display extended properties.
  81 + for extended_property in entry.extended_property:
  82 + if extended_property.value:
  83 + value = extended_property.value
  84 + else:
  85 + value = extended_property.GetXmlBlobString()
  86 + print ' Extended Property %s: %s' % (extended_property.name, value)
  87 + return len(feed.entry) + ctr
  88 +
  89 + def PrintPaginatedFeed(self, feed, print_method):
  90 + """ Print all pages of a paginated feed.
  91 +
  92 + This will iterate through a paginated feed, requesting each page and
  93 + printing the entries contained therein.
  94 +
  95 + Args:
  96 + feed: A gdata.contacts.ContactsFeed instance.
  97 + print_method: The method which will be used to print each page of the
  98 + feed. Must accept these two named arguments:
  99 + feed: A gdata.contacts.ContactsFeed instance.
  100 + ctr: [int] The number of entries in this feed previously
  101 + printed. This allows continuous entry numbers when paging
  102 + through a feed.
  103 + """
  104 + ctr = 0
  105 + while feed:
  106 + # Print contents of current feed
  107 + ctr = print_method(feed=feed, ctr=ctr)
  108 + # Prepare for next feed iteration
  109 + next = feed.GetNextLink()
  110 + feed = None
  111 + if next:
  112 + if self.PromptOperationShouldContinue():
  113 + # Another feed is available, and the user has given us permission
  114 + # to fetch it
  115 + feed = self.gd_client.GetContactsFeed(next.href)
  116 + else:
  117 + # User has asked us to terminate
  118 + feed = None
  119 +
  120 + def PromptOperationShouldContinue(self):
  121 + """ Display a "Continue" prompt.
  122 +
  123 + This give is used to give users a chance to break out of a loop, just in
  124 + case they have too many contacts/groups.
  125 +
  126 + Returns:
  127 + A boolean value, True if the current operation should continue, False if
  128 + the current operation should terminate.
  129 + """
  130 + while True:
  131 + input = raw_input("Continue [Y/n]? ")
  132 + if input is 'N' or input is 'n':
  133 + return False
  134 + elif input is 'Y' or input is 'y' or input is '':
  135 + return True
  136 +
  137 + def ListAllContacts(self):
  138 + """Retrieves a list of contacts and displays name and primary email."""
  139 + feed = self.gd_client.GetContactsFeed()
  140 + self.PrintPaginatedFeed(feed, self.PrintGroupsFeed)
  141 +
  142 + def PrintGroupsFeed(self, feed, ctr):
  143 + if not feed.entry:
  144 + print '\nNo groups in feed.\n'
  145 + return 0
  146 + for i, entry in enumerate(feed.entry):
  147 + print '\n%s %s' % (ctr+i+1, entry.title.text)
  148 + if entry.content:
  149 + print ' %s' % (entry.content.text)
  150 + # Display the group id which can be used to query the contacts feed.
  151 + print ' Group ID: %s' % entry.id.text
  152 + # Display extended properties.
  153 + for extended_property in entry.extended_property:
  154 + if extended_property.value:
  155 + value = extended_property.value
  156 + else:
  157 + value = extended_property.GetXmlBlobString()
  158 + print ' Extended Property %s: %s' % (extended_property.name, value)
  159 + return len(feed.entry) + ctr
  160 +
  161 + def ListAllGroups(self):
  162 + feed = self.gd_client.GetGroupsFeed()
  163 + self.PrintPaginatedFeed(feed, self.PrintGroupsFeed)
  164 +
  165 + def CreateMenu(self):
  166 + """Prompts that enable a user to create a contact."""
  167 + name = raw_input('Enter contact\'s name: ')
  168 + notes = raw_input('Enter notes for contact: ')
  169 + primary_email = raw_input('Enter primary email address: ')
  170 +
  171 + new_contact = gdata.contacts.ContactEntry(title=atom.Title(text=name))
  172 + new_contact.content = atom.Content(text=notes)
  173 + # Create a work email address for the contact and use as primary.
  174 + new_contact.email.append(gdata.contacts.Email(address=primary_email,
  175 + primary='true', rel=gdata.contacts.REL_WORK))
  176 + entry = self.gd_client.CreateContact(new_contact)
  177 +
  178 + if entry:
  179 + print 'Creation successful!'
  180 + print 'ID for the new contact:', entry.id.text
  181 + else:
  182 + print 'Upload error.'
  183 +
  184 + def QueryMenu(self):
  185 + """Prompts for updated-min query parameters and displays results."""
  186 + updated_min = raw_input(
  187 + 'Enter updated min (example: 2007-03-16T00:00:00): ')
  188 + query = gdata.contacts.service.ContactsQuery()
  189 + query.updated_min = updated_min
  190 + feed = self.gd_client.GetContactsFeed(query.ToUri())
  191 + self.PrintFeed(feed)
  192 +
  193 + def QueryGroupsMenu(self):
  194 + """Prompts for updated-min query parameters and displays results."""
  195 + updated_min = raw_input(
  196 + 'Enter updated min (example: 2007-03-16T00:00:00): ')
  197 + query = gdata.service.Query(feed='/m8/feeds/groups/default/full')
  198 + query.updated_min = updated_min
  199 + feed = self.gd_client.GetGroupsFeed(query.ToUri())
  200 + self.PrintGroupsFeed(feed)
  201 +
  202 + def _SelectContact(self):
  203 + feed = self.gd_client.GetContactsFeed()
  204 + self.PrintFeed(feed)
  205 + selection = 5000
  206 + while selection > len(feed.entry)+1 or selection < 1:
  207 + selection = int(raw_input(
  208 + 'Enter the number for the contact you would like to modify: '))
  209 + return feed.entry[selection-1]
  210 +
  211 + def UpdateContactMenu(self):
  212 + selected_entry = self._SelectContact()
  213 + new_name = raw_input('Enter a new name for the contact: ')
  214 + if not selected_entry.title:
  215 + selected_entry.title = atom.Title()
  216 + selected_entry.title.text = new_name
  217 + self.gd_client.UpdateContact(selected_entry.GetEditLink().href, selected_entry)
  218 +
  219 + def DeleteContactMenu(self):
  220 + selected_entry = self._SelectContact()
  221 + self.gd_client.DeleteContact(selected_entry.GetEditLink().href)
  222 +
  223 + def PrintMenu(self):
  224 + """Displays a menu of options for the user to choose from."""
  225 + print ('\nContacts Sample\n'
  226 + '1) List all of your contacts.\n'
  227 + '2) Create a contact.\n'
  228 + '3) Query contacts on updated time.\n'
  229 + '4) Modify a contact.\n'
  230 + '5) Delete a contact.\n'
  231 + '6) List all of your contact groups.\n'
  232 + '7) Query your groups on updated time.\n'
  233 + '8) Exit.\n')
  234 +
  235 + def GetMenuChoice(self, max):
  236 + """Retrieves the menu selection from the user.
  237 +
  238 + Args:
  239 + max: [int] The maximum number of allowed choices (inclusive)
  240 +
  241 + Returns:
  242 + The integer of the menu item chosen by the user.
  243 + """
  244 + while True:
  245 + input = raw_input('> ')
  246 +
  247 + try:
  248 + num = int(input)
  249 + except ValueError:
  250 + print 'Invalid choice. Please choose a value between 1 and', max
  251 + continue
  252 +
  253 + if num > max or num < 1:
  254 + print 'Invalid choice. Please choose a value between 1 and', max
  255 + else:
  256 + return num
  257 +
  258 + def Run(self):
  259 + """Prompts the user to choose funtionality to be demonstrated."""
  260 + try:
  261 + while True:
  262 +
  263 + self.PrintMenu()
  264 +
  265 + choice = self.GetMenuChoice(8)
  266 +
  267 + if choice == 1:
  268 + self.ListAllContacts()
  269 + elif choice == 2:
  270 + self.CreateMenu()
  271 + elif choice == 3:
  272 + self.QueryMenu()
  273 + elif choice == 4:
  274 + self.UpdateContactMenu()
  275 + elif choice == 5:
  276 + self.DeleteContactMenu()
  277 + elif choice == 6:
  278 + self.ListAllGroups()
  279 + elif choice == 7:
  280 + self.QueryGroupsMenu()
  281 + elif choice == 8:
  282 + return
  283 +
  284 + except KeyboardInterrupt:
  285 + print '\nGoodbye.'
  286 + return
  287 +
  288 +
  289 +def main():
  290 + """Demonstrates use of the Contacts extension using the ContactsSample object."""
  291 + # Parse command line options
  292 + try:
  293 + opts, args = getopt.getopt(sys.argv[1:], '', ['user=', 'pw='])
  294 + except getopt.error, msg:
  295 + print 'python contacts_example.py --user [username] --pw [password]'
  296 + sys.exit(2)
  297 +
  298 + user = ''
  299 + pw = ''
  300 + # Process options
  301 + for option, arg in opts:
  302 + if option == '--user':
  303 + user = arg
  304 + elif option == '--pw':
  305 + pw = arg
  306 +
  307 + while not user:
  308 + print 'NOTE: Please run these tests only with a test account.'
  309 + user = raw_input('Please enter your username: ')
  310 + while not pw:
  311 + pw = getpass.getpass()
  312 + if not pw:
  313 + print 'Password cannot be blank.'
  314 +
  315 +
  316 + try:
  317 + sample = ContactsSample(user, pw)
  318 + except gdata.service.BadAuthentication:
  319 + print 'Invalid user credentials given.'
  320 + return
  321 +
  322 + sample.Run()
  323 +
  324 +
  325 +if __name__ == '__main__':
  326 + main()
153 nq.py
... ... @@ -0,0 +1,153 @@
  1 +#!/usr/bin/python
  2 +#
  3 +# Copyright (c) 2011 Mark D. Pesce
  4 +#
  5 +# Permission is hereby granted, free of charge, to any person obtaining a copy
  6 +# of this software and associated documentation files (the "Software"), to deal
  7 +# in the Software without restriction, including without limitation the rights
  8 +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9 +# copies of the Software, and to permit persons to whom the Software is
  10 +# furnished to do so, subject to the following conditions:
  11 +#
  12 +# The above copyright notice and this permission notice shall be included in
  13 +# all copies or substantial portions of the Software.
  14 +#
  15 +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16 +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17 +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18 +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19 +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20 +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21 +# THE SOFTWARE.
  22 +#
  23 +# This is NQ, the enqueueing module
  24 +# This module receives all of the things to be shared
  25 +# And keeps them in its own database
  26 +# So that when front end clients ask for them
  27 +# Well, here they are!
  28 +#
  29 +# Always call the send_shared() function
  30 +# And the rest of the work happens internally here
  31 +
  32 +import os
  33 +import threading
  34 +import sqlite3
  35 +import sharer # module that actually dispatches the thingy to be shared
  36 +
  37 +dbfname = 'nq.db' # Name of the NQ database, sensibly
  38 +semi = threading.Semaphore() # Start us up, with (we hope) a global semaphore
  39 +
  40 +# Returns True if we are running on Android - use absolute paths
  41 +def isAndroid():
  42 + try:
  43 + import android
  44 + #print "Android!"
  45 + return True
  46 + except:
  47 + return False
  48 +
  49 +def getDBName():
  50 + if isAndroid():
  51 + return "/sdcard/sl4a/scripts/db/v2/" + dbfname
  52 + else:
  53 + return 'db' + os.sep + dbfname
  54 +
  55 +def open_listener_db():
  56 +
  57 + # We want to open the database of listened-to stuffs
  58 + # We need to use the semaphore to make sure we really can do that
  59 + #print('Acquiring semaphore...')
  60 + semi.acquire()
  61 + #print('Acquired semaphore')
  62 +
  63 + try:
  64 + testy = os.stat(getDBName())
  65 + except OSError: # No directory, apparently
  66 + if (isAndroid()):
  67 + try:
  68 + os.mkdir('/sdcard/sl4a/scripts/db')
  69 + print 'Created Android db directory'
  70 + except OSError:
  71 + #print 'Directory db already exists, will not create it'
  72 + x = 1 # placeholder
  73 + else:
  74 + try:
  75 + os.mkdir('db')
  76 + print('Created db directory')
  77 + except OSError:
  78 + #print('Directory db already exists, will not create it')
  79 + x = 2 # placeholder
  80 +
  81 + # Try to connect to the plexbase.
  82 + # If we can't, we need to create it.
  83 + # We return a connection object thingy
  84 + try:
  85 + #print('Trying to connect')
  86 + connector = sqlite3.connect(getDBName())
  87 + #print('Successful connection')
  88 + # Do we know if we have the correct table in this database?
  89 + # Or any tables at all?
  90 + connector.execute('''create table if not exists shared (msgid text, type text, pluid text, timestamp text, data text)''')
  91 + except:
  92 + print('Some sort of exception connecting to listenbase')
  93 + connector = None
  94 + return connector
  95 +
  96 +def close_listener_db(connector):
  97 + connector.commit()
  98 + connector.close()
  99 + semi.release()
  100 + #print('Semaphore released')
  101 +
  102 +def send_shared(msgid, type, pluid, timestamp, data):
  103 + print("We want to share to something!")
  104 + print msgid, type, timestamp, data
  105 + connector = open_listener_db()
  106 + if (connector != None):
  107 + connector.execute('insert into shared values (?,?,?,?,?)', (msgid, type, pluid, timestamp, data))
  108 + close_listener_db(connector)
  109 +
  110 + # Our work has just barely begun
  111 + # This is where we actually map a message type to a particular sharer.
  112 + # We should be prepared to send it off to the appropriate sharer, once we reckon which sharer that is.
  113 + # Now when this gets all fancy-like, it will handle failure recovery, etc.
  114 + sharer.share(msgid, type, pluid, timestamp, data)
  115 +
  116 +def get_shared(index):
  117 + #print("We are getting index " + str(index))
  118 + #the_index = int(index)
  119 + connector = open_listener_db()
  120 + if (connector != None):
  121 + curs = connector.cursor()
  122 + curs.execute('select * from shared where ROWID=?',(index,))
  123 + result = curs.fetchone()
  124 + # Here we would take the row data and put it into retobj
  125 + retobj = None
  126 + close_listener_db(connector)
  127 + return retobj
  128 + else:
  129 + return None
  130 +
  131 +def size_listened(index):
  132 + #print("We are getting the size")
  133 + the_index = int(index)
  134 + connector = open_listener_db()
  135 + if (connector != None):
  136 + curs = connector.cursor()
  137 + curs.execute('select * from shared where ROWID=?',(the_index,))
  138 + result = curs.fetchone()
  139 + # Need a size calcualtion right here
  140 + close_listener_db(connector)
  141 + return retsize
  142 + else:
  143 + return 0
  144 +
  145 +def count_listened():
  146 + #print("count_listened")
  147 + connector = open_listener_db()
  148 + if (connector != None):
  149 + curs = connector.cursor()
  150 + curs.execute('select ROWID from shared') # Cheap and fast
  151 + count = len(curs.fetchall())
  152 + close_listener_db(connector)
  153 + return count
43 plexus_smtp.py
@@ -32,7 +32,7 @@
32 32 from email.parser import Parser, HeaderParser
33 33 import email
34 34 import socket
35   -import dq
  35 +import dq, nq
36 36 import plxaddr
37 37 import plex
38 38
@@ -182,28 +182,6 @@ def process_message(msg):
182 182 print "Garbled headers, rejecting"
183 183 return
184 184
185   -# msgid = msg['Subject'] # Should have a UUID therein - some way to validate this?
186   -#
187   -# mfrom = msg['From'] # Get the addressee information
188   -# f = unpack_sender(mfrom)
189   -# print f
190   -#
191   -# mto = msg['To']
192   -# to = unpack_to(mto)
193   -# print to
194   -
195   -# if validate_type(f['type']):
196   -# print "Type valid"
197   -# else:
198   -# print "Type invalid"
199   -# return
200   -
201   -# if validate_type(ph['plexus-identifier']):
202   -# print "Plexus Identifier valid"
203   -# else:
204   -# print "Plexus Identifier invalid"
205   -# return
206   -
207 185 contents = msg.get_payload()
208 186 if validate_payload(contents):
209 187 print "Contents valid"
@@ -213,25 +191,34 @@ def process_message(msg):
213 191 content_object = json.loads(contents) # Now we have a lovely object with stuff in it
214 192
215 193 # Is there anything more fun than a state machine? I thought not.
216   -# state = f['type']
217 194 if (ph['listened']):
218 195 print "We are listening, so we DQ this"
219 196 state = ph['plexus-identifier']
220 197 if (state.find(u'plexus-update') == 0): # Twitter updates, etc.
221   - print "UPDATE"
  198 + print "LISTENED UPDATE"
222 199 print "Sending it to the DQ"
223 200 pluid = contents_to_pluid(contents)
224 201 dq.send_listened(ph['tracking_id'], state, pluid, content_object['when'], contents) # Pop it onto the DQ
225 202 elif (state.find(u'plexus-message') == 0): # Twitter DMs, emails, FB messages, etc.
226   - print "MESSAGE"
  203 + print "LISTENED MESSAGE"
227 204 pluid = contents_to_pluid(contents)
228 205 dq.send_listened(ph['tracking_id'], state, pluid, content_object['when'], contents) # Pop it onto the DQ
229 206 elif (state.find(u'plexus-post') == 0): # RSS, for example
230   - print "POST"
  207 + print "LISTENED POST"
231 208 elif (state.find(u'plexus-command') == 0): # Plexus commands <- very important
232   - print "COMMAND"
  209 + print "LISTENED COMMAND"
233 210 else:
234 211 print "We are sharing, so we NQ this"
  212 + state = ph['plexus-identifier']
  213 + if (state.find(u'plexus-update') == 0): # Twitter updates, etc.
  214 + print "SHARED UPDATE"
  215 + print "Sending it to the DQ"
  216 + nq.send_shared(ph['tracking_id'], state, "", content_object['when'], contents) # Pop it onto the NQ
  217 + elif (state.find(u'plexus-message') == 0): # Twitter DMs, emails, FB messages, etc.
  218 + print "SHARED MESSAGE"
  219 + pluid = contents_to_pluid(contents)
  220 + nq.send_shared(ph['tracking_id'], state, "", content_object['when'], contents) # Pop it onto the NQ
  221 +
235 222
236 223 if __name__ == "__main__":
237 224
262 profiles_example.py
... ... @@ -0,0 +1,262 @@
  1 +#!/usr/bin/env python
  2 +#
  3 +# Copyright 2009 Google Inc. All Rights Reserved.
  4 +#
  5 +# Licensed under the Apache License, Version 2.0 (the "License");
  6 +# you may not use this file except in compliance with the License.
  7 +# You may obtain a copy of the License at
  8 +#
  9 +# http://www.apache.org/licenses/LICENSE-2.0
  10 +#
  11 +# Unless required by applicable law or agreed to in writing, software
  12 +# distributed under the License is distributed on an "AS IS" BASIS,
  13 +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 +# See the License for the specific language governing permissions and
  15 +# limitations under the License.
  16 +
  17 +"""Contains a Sample for Google Profiles.
  18 +
  19 + ProfilesSample: demonstrates operations with the Profiles feed.
  20 +"""
  21 +
  22 +__author__ = 'jtoledo (Julian Toledo)'
  23 +
  24 +
  25 +import getopt
  26 +import getpass
  27 +import sys
  28 +
  29 +import gdata.contacts
  30 +import gdata.contacts.service
  31 +
  32 +
  33 +class ProfilesSample(object):
  34 + """ProfilesSample object demonstrates operations with the Profiles feed."""
  35 +
  36 + def __init__(self, email, password, domain):
  37 + """Constructor for the ProfilesSample object.
  38 +
  39 + Takes an email and password corresponding to a gmail account to
  40 + demonstrate the functionality of the Profiles feed.
  41 +
  42 + Args:
  43 + email: [string] The e-mail address of the account to use for the sample.
  44 + password: [string] The password corresponding to the account specified by
  45 + the email parameter.
  46 + domain: [string] The domain for the Profiles feed
  47 + """
  48 + self.gd_client = gdata.contacts.service.ContactsService(
  49 + contact_list=domain)
  50 + self.gd_client.email = email
  51 + self.gd_client.password = password
  52 + self.gd_client.source = 'GoogleInc-ProfilesPythonSample-1'
  53 + self.gd_client.ProgrammaticLogin()
  54 +
  55 + def PrintFeed(self, feed, ctr=0):
  56 + """Prints out the contents of a feed to the console.
  57 +
  58 + Args:
  59 + feed: A gdata.profiles.ProfilesFeed instance.
  60 + ctr: [int] The number of entries in this feed previously printed. This
  61 + allows continuous entry numbers when paging through a feed.
  62 +
  63 + Returns:
  64 + The number of entries printed, including those previously printed as
  65 + specified in ctr. This is for passing as an ar1gument to ctr on
  66 + successive calls to this method.
  67 + """
  68 + if not feed.entry:
  69 + print '\nNo entries in feed.\n'
  70 + return 0
  71 + for entry in feed.entry:
  72 + self.PrintEntry(entry)
  73 + return len(feed.entry) + ctr
  74 +
  75 + def PrintEntry(self, entry):
  76 + """Prints out the contents of a single Entry to the console.
  77 +
  78 + Args:
  79 + entry: A gdata.contacts.ProfilesEntry
  80 + """
  81 + print '\n%s' % (entry.title.text)
  82 + for email in entry.email:
  83 + if email.primary == 'true':
  84 + print 'Email: %s (primary)' % (email.address)
  85 + else:
  86 + print 'Email: %s' % (email.address)
  87 + if entry.nickname:
  88 + print 'Nickname: %s' % (entry.nickname.text)
  89 + if entry.occupation:
  90 + print 'Occupation: %s' % (entry.occupation.text)
  91 + if entry.gender:
  92 + print 'Gender: %s' % (entry.gender.value)
  93 + if entry.birthday:
  94 + print 'Birthday: %s' % (entry.birthday.when)
  95 + for relation in entry.relation:
  96 + print 'Relation: %s %s' % (relation.rel, relation.text)
  97 + for user_defined_field in entry.user_defined_field:
  98 + print 'UserDefinedField: %s %s' % (user_defined_field.key,
  99 + user_defined_field.value)
  100 + for website in entry.website:
  101 + print 'Website: %s %s' % (website.href, website.rel)
  102 + for phone_number in entry.phone_number:
  103 + print 'Phone Number: %s' % phone_number.text
  104 + for organization in entry.organization:
  105 + print 'Organization:'
  106 + if organization.org_name:
  107 + print ' Name: %s' % (organization.org_name.text)
  108 + if organization.org_title:
  109 + print ' Title: %s' % (organization.org_title.text)
  110 + if organization.org_department:
  111 + print ' Department: %s' % (organization.org_department.text)
  112 + if organization.org_job_description:
  113 + print ' Job Desc: %s' % (organization.org_job_description.text)
  114 +
  115 + def PrintPaginatedFeed(self, feed, print_method):
  116 + """Print all pages of a paginated feed.
  117 +
  118 + This will iterate through a paginated feed, requesting each page and
  119 + printing the entries contained therein.
  120 +
  121 + Args:
  122 + feed: A gdata.contacts.ProfilesFeed instance.
  123 + print_method: The method which will be used to print each page of the
  124 + """
  125 + ctr = 0
  126 + while feed:
  127 + # Print contents of current feed
  128 + ctr = print_method(feed=feed, ctr=ctr)
  129 + # Prepare for next feed iteration
  130 + next = feed.GetNextLink()
  131 + feed = None
  132 + if next:
  133 + if self.PromptOperationShouldContinue():
  134 + # Another feed is available, and the user has given us permission
  135 + # to fetch it
  136 + feed = self.gd_client.GetProfilesFeed(next.href)
  137 + else:
  138 + # User has asked us to terminate
  139 + feed = None
  140 +
  141 + def PromptOperationShouldContinue(self):
  142 + """Display a "Continue" prompt.
  143 +
  144 + This give is used to give users a chance to break out of a loop, just in
  145 + case they have too many profiles/groups.
  146 +
  147 + Returns:
  148 + A boolean value, True if the current operation should continue, False if
  149 + the current operation should terminate.
  150 + """
  151 + while True:
  152 + key_input = raw_input('Continue [Y/n]? ')
  153 + if key_input is 'N' or key_input is 'n':
  154 + return False
  155 + elif key_input is 'Y' or key_input is 'y' or key_input is '':
  156 + return True
  157 +
  158 + def ListAllProfiles(self):
  159 + """Retrieves a list of profiles and displays name and primary email."""
  160 + feed = self.gd_client.GetProfilesFeed()
  161 + self.PrintPaginatedFeed(feed, self.PrintFeed)
  162 +
  163 + def SelectProfile(self):
  164 + username = raw_input('Please enter your username for the profile: ')
  165 + entry_uri = self.gd_client.GetFeedUri('profiles')+'/'+username
  166 + try:
  167 + entry = self.gd_client.GetProfile(entry_uri)
  168 + self.PrintEntry(entry)
  169 + except gdata.service.RequestError:
  170 + print 'Invalid username for the profile.'
  171 +
  172 + def PrintMenu(self):
  173 + """Displays a menu of options for the user to choose from."""
  174 + print ('\nProfiles Sample\n'
  175 + '1) List all of your Profiles.\n'
  176 + '2) Get a single Profile.\n'
  177 + '3) Exit.\n')
  178 +
  179 + def GetMenuChoice(self, maximum):
  180 + """Retrieves the menu selection from the user.
  181 +
  182 + Args:
  183 + maximum: [int] The maximum number of allowed choices (inclusive)
  184 +
  185 + Returns:
  186 + The integer of the menu item chosen by the user.
  187 + """
  188 + while True:
  189 + key_input = raw_input('> ')
  190 +
  191 + try:
  192 + num = int(key_input)
  193 + except ValueError:
  194 + print 'Invalid choice. Please choose a value between 1 and', maximum
  195 + continue
  196 +
  197 + if num > maximum or num < 1:
  198 + print 'Invalid choice. Please choose a value between 1 and', maximum
  199 + else:
  200 + return num
  201 +
  202 + def Run(self):
  203 + """Prompts the user to choose funtionality to be demonstrated."""
  204 + try:
  205 + while True:
  206 + self.PrintMenu()
  207 + choice = self.GetMenuChoice(3)
  208 + if choice == 1:
  209 + self.ListAllProfiles()
  210 + elif choice == 2:
  211 + self.SelectProfile()
  212 + elif choice == 3:
  213 + return
  214 +
  215 + except KeyboardInterrupt:
  216 + print '\nGoodbye.'
  217 + return
  218 +
  219 +
  220 +def main():
  221 + """Demonstrates use of the Profiles using the ProfilesSample object."""
  222 + # Parse command line options
  223 + try:
  224 + opts, args = getopt.getopt(sys.argv[1:], '', ['user=', 'pw=', 'domain='])
  225 + except getopt.error, msg:
  226 + print 'python profiles_example.py --user [username] --pw [password]'
  227 + print ' --domain [domain]'
  228 + sys.exit(2)
  229 +
  230 + user = ''
  231 + pw = ''
  232 + domain = ''
  233 +
  234 + # Process options
  235 + for option, arg in opts:
  236 + if option == '--user':
  237 + user = arg
  238 + elif option == '--pw':
  239 + pw = arg
  240 + elif option == '--domain':
  241 + domain = arg
  242 +
  243 + while not user:
  244 + print 'NOTE: Please run these tests only with a test account.'
  245 + user = raw_input('Please enter your email: ')
  246 + while not pw:
  247 + pw = getpass.getpass('Please enter password: ')
  248 + if not pw:
  249 + print 'Password cannot be blank.'
  250 + while not domain:
  251 + domain = raw_input('Please enter your Apps domain: ')
  252 +
  253 + try:
  254 + sample = ProfilesSample(user, pw, domain)
  255 + except gdata.service.BadAuthentication:
  256 + print 'Invalid user credentials given.'
  257 + return
  258 +
  259 + sample.Run()
  260 +
  261 +if __name__ == '__main__':
  262 + main()
171 sharer.py
... ... @@ -0,0 +1,171 @@
  1 +#!/usr/bin/python
  2 +#
  3 +# Copyright (c) 2010, 2011 Mark D. Pesce
  4 +#
  5 +# Permission is hereby granted, free of charge, to any person obtaining a copy
  6 +# of this software and associated documentation files (the "Software"), to deal
  7 +# in the Software without restriction, including without limitation the rights
  8 +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9 +# copies of the Software, and to permit persons to whom the Software is
  10 +# furnished to do so, subject to the following conditions:
  11 +#
  12 +# The above copyright notice and this permission notice shall be included in
  13 +# all copies or substantial portions of the Software.
  14 +#
  15 +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16 +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17 +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18 +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19 +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20 +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21 +# THE SOFTWARE.
  22 +#
  23 +# This is the main database module for Plexus
  24 +# Which handles storage and retrieval of entries in the social graph
  25 +
  26 +import sqlite3 # Requires 2.5
  27 +import os
  28 +import json # Requires 2.6
  29 +import uuid # Requires 2.5
  30 +import smtplib
  31 +from email.mime.text import MIMEText
  32 +
  33 +instance_id = 'e309f818-f870-40ff-ba7a-8ff69a4bbe28' # This should be provided programatically, but whatevs...
  34 +
  35 +dbfname = 'sharer.db' # Name of the Plexus sharer map database, sensibly
  36 +# Returns True if we are running on Android - use absolute paths
  37 +def isAndroid():
  38 + try:
  39 + import android
  40 + return True
  41 + except:
  42 + return False
  43 +
  44 +def getDBName():
  45 + if isAndroid():
  46 + return "/sdcard/sl4a/scripts/v2/db/" + dbfname
  47 + else:
  48 + return 'db' + os.sep + dbfname
  49 +
  50 +class Sharer:
  51 + # The Sharer instances itself on connection
  52 + def __init__(self):
  53 + connector = self.connect()
  54 + if (connector == None):
  55 + print('This ain\'t gonna work right.')
  56 + # Probably should throw an exception
  57 + raise OSError
  58 + return
  59 +
  60 + def connect(self):
  61 + try:
  62 + testy = os.stat(getDBName())
  63 + except OSError: # No directory, apparently
  64 + try:
  65 + if isAndroid():
  66 + os.mkdir('/sdcard/sl4a/scripts/v2/db')
  67 + print 'Created Android db directory'
  68 + else:
  69 + os.mkdir('db') # Relative location
  70 + print 'Created db directory'
  71 + except OSError:
  72 + print('Directory db already exists, will not create it')
  73 +
  74 + # Try to connect to the Sharer.
  75 + # If we can't, we need to create it.
  76 + # We return a connection object thingy
  77 + try:
  78 + #print('Trying to connect')
  79 + self.connector = sqlite3.connect(getDBName())
  80 + #print('Successful connection')
  81 + # Do we know if we have the correct table in this database?
  82 + # Or any tables at all?
  83 + self.connector.execute('''create table if not exists share_map (type text, service text, host text, port text, dest_id text, pluid text)''')
  84 + return self.connector
  85 + except:
  86 + print('Some sort of exception connecting to share_map')
  87 + self.connector = None
  88 + return self.connector
  89 +
  90 + def close(self):
  91 + self.connector.commit() # Commit all the changes to the plexbase
  92 + self.connector.close()
  93 +
  94 +def beam(msgid, type, pluid, timestamp, data, host, port, dest_id):
  95 + print 'We are BEAMING to %s on port %s' % (host, port)
  96 +
  97 + # Eventually a function will handle this reasonably.
  98 + # For the moment, a constant
  99 + my_name = 'mpesce'
  100 +
  101 + # Now build the RFC2822/5822 message
  102 + msg = MIMEText(data)
  103 + msg.set_charset('utf-8')
  104 + msg['From'] = my_name + "." + instance_id + "@plexus.relationalspace.org" # That should be unique and global across Plexus
  105 + msg['To'] = "plexus-update." + dest_id + "@plexus.relationalspace.org" # Routing information for message type
  106 + msg['Subject'] = msgid # unique ID for tracking messages
  107 +
  108 + # To transport by SMTP, we wrap our RFC2822-compliant message in another header.
  109 + # This allows us to have our way with the sender and receiver fields. Muahahah.
  110 + print "Sending mail..."
  111 + smtp_msg = MIMEText(msg.as_string())
  112 + smtp_msg.set_charset('utf-8')
  113 + smtp_msg['Subject'] = "sharer.py"
  114 +
  115 + try:
  116 + s = smtplib.SMTP(host, int(port)) # of course, this could be running anywhere, really
  117 + s.sendmail("mark@markpesce.com", "mpesce@gmail.com", smtp_msg.as_string())
  118 + s.quit()
  119 + print 'Message sent!'
  120 + except:
  121 + print 'We threw an exception trying to send that. Probably the host is not there or refused. Sorry.'
  122 +
  123 +# This is the entry point for anything that wants to share something
  124 +def share(msgid, type, pluid, timestamp, data):
  125 + print 'We should be sharing right now...'
  126 +
  127 + # First off, let's find out if there is a specific sharer for this pluid
  128 + # Erm, do we have a pluid?
  129 + if (len(pluid) > 0):
  130 + # Well, if we do, we don't know what to do with it. Yet.
  131 + print 'We have a pluid, vague hand-waving motions...'
  132 + else:
  133 +
  134 + # Let's find the mapping(s) for this type.
  135 + # What do we do if we have multiple mappings? Probably we use them all. For now.
  136 + base = Sharer()
  137 + curs = base.connector.cursor()
  138 + matches = curs.execute('select * from share_map where type=?', (type,))
  139 + resultant = curs.fetchall()
  140 + if (len(resultant) == 0):
  141 + print 'No matching types, which is weird and PROBABLY VERY WRONG, aborting...'
  142 + return
  143 + else:
  144 + for method in resultant:
  145 + beam(msgid, type, pluid, timestamp, data, method[2], method[3], method[4]) # Should send things right along. Probably.
  146 + return
  147 +
  148 +# Given a pluid and plexus type, returns (service, pointer) if one exists
  149 +# This is necessary if there are specific share_map entries for specific social graph entries
  150 +def match_service(pluid, type):
  151 + base = Sharer()
  152 + curs = base.connector.cursor()
  153 + matches = curs.execute('select * from share_map where pluid=? and type=?', (pluid, type))
  154 + resultant = curs.fetchone()
  155 + base.close()
  156 + if (resultant != None):
  157 + return((resultant[1],resultant[2]))
  158 + else:
  159 + return None
  160 +
  161 +# Create a new share map, should run this before having a play, fills with data -- used during development ONLY
  162 +def init_share_map():
  163 + base = Sharer()
  164 + curs = base.connector.cursor()
  165 + curs.execute('''insert into share_map values ("plexus-message", "smtp", "localhost", "4180", "bbb3af78-7e94-4dd1-b15a-c1ee3527a018", "")''')
  166 + curs.execute('''insert into share_map values ("plexus-update", "twitter", "localhost", "4181", "5d686217-fbe8-4d72-9440-db2da08b45a6", "")''')
  167 + base.close()
  168 + print 'New share_map database created.'
  169 +
  170 +if __name__ == "__main__":
  171 + init_share_map()
118 twitter_listener.py
@@ -228,12 +228,12 @@ def mail_msg(txt):
228 228 msg['Subject'] = "twitter_listener.py"
229 229 s = smtplib.SMTP()
230 230 s.set_debuglevel(False)
231   - s.connect("smtp.gmail.com", 587)
  231 + s.connect("smtp.smtphost.tld", 587) # smtp.google.com, for example
232 232 s.ehlo()
233 233 s.starttls()
234 234 s.ehlo()
235   - s.login("mpesce@gmail.com", "********")
236   - s.sendmail("mark@markpesce.com", "mpesce@gmail.com", msg.as_string())
  235 + s.login("user@smtphost.tld", "password")
  236 + s.sendmail("to@hostname.tld", "from@hostname.tld", msg.as_string())
237 237 s.close()
238 238
239 239
@@ -257,25 +257,8 @@ def get_my_screen_name(api):
257 257 for status in statuses:
258 258 screen_name = status.user.screen_name
259 259 return screen_name
260   -
261 260
262   -# def make_msg(api, screen_name, update):
263   -# plexus_data = { "service": "twitter", "update": update }
264   -# #mime_header ="MIME-Version 1.0\nContent-Transfer-Encoding: 7bit\nContent-Type: plexus/update; charset=\"utf-8\"\n\n"
265   -# msg = MIMEText(json.dumps(plexus_data))
266   -# msg.set_charset('utf-8')
267   -# msg['Subject'] = str(uuid.uuid4())
268   -#
269   -# # Create the 'From' field by signing the UUID of the module with the private key
270   -# #signature = rsa.sign(listener_uuid, keys['priv'])
271   -# #print signature
272   -# # Get the user to put into 'From' field
273   -# msg['From'] = screen_name.encode('utf-8')
274   -# msg['To'] = "TO BE COMPLETED"
275   -#
276   -# return msg
277   -
278   -if __name__ == "__main__":
  261 +def main():
279 262
280 263 # Check to see if we have a keypair
281 264 # keys = getPubPriv()
@@ -291,8 +274,6 @@ def get_my_screen_name(api):
291 274 print "You don't have Twitter authorization, let's do that now..."
292 275 authorize()
293 276 credentials = getLogin()
294   -# else:
295   -# print "We already have authorization, so let's grab some statuses..."
296 277
297 278 # Get the Instance ID
298 279 instance_id = getInstance()
@@ -326,50 +307,67 @@ def get_my_screen_name(api):
326 307 # Check for DMs before we move along to the statuses (because they're more important, aren't they?)
327 308 if (dm_since == None):
328 309 print "Starting timestamp: ", starting_timestamp
329   - directs = api.GetDirectMessages()
  310 + try:
  311 + directs = api.GetDirectMessages()
  312 + except:
  313 + print 'Twitter API GetDirectMessages blew a sprocket, trying to recover gracefully'
  314 + directs = None
330 315 else:
331   - directs = api.GetDirectMessages(since_id=dm_since)
332   - for direct in directs:
333   - if dm_since < direct.id:
334   - dm_since = direct.id # Keep things current
335   -
336   - if (direct.created_at_in_seconds > starting_timestamp): # Since we started up?
337   - name_list = [ direct.sender_screen_name.encode('utf-8'), ]
338   - plexus_data = { "service": "twitter", "plexus-message": direct.text.encode('utf-8'), "source": name_list, "when": str(direct.created_at_in_seconds) }
  316 + try:
  317 + directs = api.GetDirectMessages(since_id=dm_since)
  318 + except:
  319 + print 'Twitter API GetDirectMessages blew a sprocket, trying to recover gracefully'
  320 + directs = None
  321 +
  322 + if (directs != None):
  323 + for direct in directs:
  324 + if dm_since < direct.id:
  325 + dm_since = direct.id # Keep things current
  326 +
  327 + if (direct.created_at_in_seconds > starting_timestamp): # Since we started up?
  328 + name_list = [ direct.sender_screen_name.encode('utf-8'), ]
  329 + plexus_data = { "service": "twitter", "plexus-message": direct.text.encode('utf-8'), "source": name_list, "when": str(direct.created_at_in_seconds) }
  330 + msg = MIMEText(json.dumps(plexus_data))
  331 + msg.set_charset('utf-8')
  332 + msg['To'] = screen_name.encode('utf-8') + "." + dest_id + "@plexus.relationalspace.org" # That should be unique and global across Plexus
  333 + msg['From'] = "plexus-message." + instance_id + "@plexus.relationalspace.org" # Routing information for message type
  334 + msg['Subject'] = str(uuid.uuid4()) # unique ID for tracking messages
  335 + print msg.as_string()
  336 + #mail_msg(msg.as_string())
  337 + mail_msg_plexus(msg.as_string())
  338 +
  339 + try:
  340 + statuses = api.GetFriendsTimeline(count=count_num, since_id=start_id)
  341 + except:
  342 + print 'Twitter API GetFriendsTimeline blew a sprocket, trying to recover gracefully'
  343 + statuses = None
  344 +
  345 + if (statuses != None):
  346 + for status in statuses:
  347 +
  348 + if start_id < status.id:
  349 + start_id = status.id # Keep latest status
  350 + #txt = yaml.dump(status)
  351 + #txt = status.user.screen_name.encode('utf-8') + ": " + status.text.encode('utf-8')
  352 + #msg = make_msg(api, screen_name, status.text.encode('utf-8'))
  353 + name_list = [ status.user.screen_name.encode('utf-8'), ]
  354 + plexus_data = { "service": "twitter", "plexus-update": status.text.encode('utf-8'), "source": name_list, "when": str(status.created_at_in_seconds) }
  355 + #mime_header ="MIME-Version 1.0\nContent-Transfer-Encoding: 7bit\nContent-Type: plexus/update; charset=\"utf-8\"\n\n"
339 356 msg = MIMEText(json.dumps(plexus_data))
340 357 msg.set_charset('utf-8')
  358 +
  359 + # Create the 'From' field by signing the UUID of the module with the private key
  360 + #signature = rsa.sign(listener_uuid, keys['priv'])
  361 + #print signature
  362 + # Get the user to put into 'From' field
341 363 msg['To'] = screen_name.encode('utf-8') + "." + dest_id + "@plexus.relationalspace.org" # That should be unique and global across Plexus
342   - msg['From'] = "plexus-message." + instance_id + "@plexus.relationalspace.org" # Routing information for message type
  364 + msg['From'] = "plexus-update." + instance_id + "@plexus.relationalspace.org" # Routing information for message type
343 365 msg['Subject'] = str(uuid.uuid4()) # unique ID for tracking messages
344   -
345 366 print msg.as_string()
346 367 #mail_msg(msg.as_string())
347 368 mail_msg_plexus(msg.as_string())
348   -
349   - statuses = api.GetFriendsTimeline(count=count_num, since_id=start_id)
350   - for status in statuses:
351   -
352   - if start_id < status.id:
353   - start_id = status.id # Keep latest status
354   - #txt = yaml.dump(status)
355   - #txt = status.user.screen_name.encode('utf-8') + ": " + status.text.encode('utf-8')
356   - #msg = make_msg(api, screen_name, status.text.encode('utf-8'))
357   - name_list = [ status.user.screen_name.encode('utf-8'), ]
358   - plexus_data = { "service": "twitter", "plexus-update": status.text.encode('utf-8'), "source": name_list, "when": str(status.created_at_in_seconds) }
359   - #mime_header ="MIME-Version 1.0\nContent-Transfer-Encoding: 7bit\nContent-Type: plexus/update; charset=\"utf-8\"\n\n"
360   - msg = MIMEText(json.dumps(plexus_data))
361   - msg.set_charset('utf-8')
362   -
363   - # Create the 'From' field by signing the UUID of the module with the private key
364   - #signature = rsa.sign(listener_uuid, keys['priv'])
365   - #print signature
366   - # Get the user to put into 'From' field
367   - msg['To'] = screen_name.encode('utf-8') + "." + dest_id + "@plexus.relationalspace.org" # That should be unique and global across Plexus
368   - msg['From'] = "plexus-update." + instance_id + "@plexus.relationalspace.org" # Routing information for message type
369   - msg['Subject'] = str(uuid.uuid4()) # unique ID for tracking messages
370   -
371   - print msg.as_string()
372   - #mail_msg(msg.as_string())
373   - mail_msg_plexus(msg.as_string())
374 369 print "Sleeping..."
375 370 time.sleep(30) # wait 30 seconds, and do it all again
  371 +
  372 +if __name__ == "__main__":
  373 + main()
4 twitter_sharer.py
@@ -374,7 +374,7 @@ def do_sendDM(stuff):
374 374 set_ip = sys.argv[1]
375 375
376 376
377   - print "Starting Plexus SMTP interface on", set_ip, "port 4180"
378   - server = PlexusSMTPServer((set_ip, 4180), None)
  377 + print "Starting Plexus SMTP interface on", set_ip, "port 4181"
  378 + server = PlexusSMTPServer((set_ip, 4181), None)
379 379 asyncore.loop()
380 380

0 comments on commit ee153ef

Please sign in to comment.
Something went wrong with that request. Please try again.