diff --git a/src/client.coffee b/src/client.coffee old mode 100644 new mode 100755 index 813637a..970b5ff --- a/src/client.coffee +++ b/src/client.coffee @@ -486,6 +486,15 @@ module.exports = class Client extends EventEmitter max_results ] + # Return information from a phone number in e164 + getentitybyphonenumber: (e164) -> + @chatreq.req('contacts/getentitybyid', [ + @_requestBodyHeader() + None + [[null, null, null, e164, null, true]], + [1, 2] + ], false).then (body) -> + CLIENT_GET_ENTITY_BY_ID_RESPONSE.parse body # Return information about a list of chat_ids getentitybyid: (chat_ids) -> @@ -567,6 +576,44 @@ module.exports = class Client extends EventEmitter log.debug 'image resume upload finished' body?.sessionStatus?.additionalInfo?['uploader_service.GoogleRupioAdditionalInfo']?.completionInfo?.customerSpecificInfo?.photoid + # Uploads an image as buffer that can be later attached to a chat message. + # + # buffer is the buffer that contains the image file content. + # + # filename is the name of the file. + # + # returns an image_id that can be used in sendchatmessage + uploadimagebuffer: (buffer, filename, timeout=30000) => + size = buffer.length; + puturl = null + chatreq = @chatreq + log.debug 'image resume upload prepare' + chatreq.baseReq IMAGE_UPLOAD_URL, 'application/x-www-form-urlencoded;charset=UTF-8' + , { + protocolVersion: "0.8" + createSessionRequest: + fields: [{ + external: { + filename, + size, + put: {}, + name: 'file' + } + }] + } + .then (body) -> + puturl = body?.sessionStatus?.externalFieldTransfers?[0]?.putInfo?.url + log.debug 'image resume upload to:', puturl + .then () -> + log.debug 'image resume uploading' + chatreq.baseReq puturl, 'application/octet-stream', buffer, true, timeout + .then (body) -> + log.debug 'image resume upload finished' + return Q.resolve body?.sessionStatus?.additionalInfo?['uploader_service.GoogleRupioAdditionalInfo']?.completionInfo?.customerSpecificInfo?.photoid + .fail (err) -> + log.error 'uploadImageBuffer failed', err + Q.reject err + # aliases list aliases = [ 'logLevel', @@ -590,7 +637,8 @@ aliases = [ 'searchEntities', 'getEntityById', 'sendEasteregg', - 'uploadImage' + 'uploadImage', + 'uploadImageBuffer' ] # set aliases diff --git a/src/pblite.coffee b/src/pblite.coffee old mode 100644 new mode 100755 index 4177215..5a7b65f --- a/src/pblite.coffee +++ b/src/pblite.coffee @@ -25,6 +25,20 @@ class EnumField return k for k, v of @enms when input == v return {} +class DictField + constructor: (dict) -> + return new DictField(dict) unless this instanceof DictField + @dict = dict + parse: (input) -> + return null unless input + return null if input == undefined + input = input.toString() if input instanceof Buffer + obj = if typeis input, 'string' then eval input else input + out = {} + for prop, val of @dict + out[prop] = val.parse obj[prop] if obj[prop] + out + class RepeatedField constructor: (field) -> return new RepeatedField(field) unless this instanceof RepeatedField @@ -53,4 +67,4 @@ class Message out[k] = v.parse val if k out -module.exports = {Field, BooleanField, EnumField, RepeatedField, Message} +module.exports = {Field, BooleanField, EnumField, DictField, RepeatedField, Message} diff --git a/src/schema.coffee b/src/schema.coffee old mode 100644 new mode 100755 index f59499b..76fa575 --- a/src/schema.coffee +++ b/src/schema.coffee @@ -1,4 +1,4 @@ -{Field, BooleanField, EnumField, RepeatedField, Message} = require './pblite' +{Field, BooleanField, EnumField, DictField, RepeatedField, Message} = require './pblite' s = {} @@ -61,19 +61,29 @@ s.ClientConversationStatus = ACTIVE : 2 LEFT : 3 - s.MessageActionType = NONE : 0 ME_ACTION : 4 - s.SegmentType = TEXT : 0 LINE_BREAK : 1 LINK : 2 +s.ItemType = + + THING : 0 + PLUS_PHOTO : 249 # Google Plus Photo + PLACE : 335 # Google Map Place + PLACE_V2 : 340 # Google Map Place v2 + +s.MediaType = + + MEDIA_TYPE_UNKNOWN : 0 + MEDIA_TYPE_PHOTO : 1 + MEDIA_TYPE_ANIMATED_PHOTO : 4 s.MembershipChangeType = @@ -258,11 +268,47 @@ s.MESSAGE_SEGMENT = Message([ ]) ]) +s.PLUS_PHOTO_THUMBNAIL = Message([ + 'url', Field() + None, Field() + None, Field() + 'image_url', Field() + None, Field() + None, Field() + None, Field() + None, Field() + None, Field() + 'width_px', Field() + 'height_px', Field() +]) + +s.PLUS_PHOTO = Message([ + 'thumbnail', s.PLUS_PHOTO_THUMBNAIL + 'owner_obfuscated_id', Field() + 'album_id', Field() + 'photo_id', Field() + None, Field() + 'url', Field() + None, Field() + None, Field() + None, Field() + 'original_content_url', Field() + None, Field() + None, Field() + 'media_type', EnumField(s.MediaType) + 'stream_id', RepeatedField(Field()) +]) + +# Special numbers make up the property names of things in the embedded item +s.EMBED_ITEM = DictField({ + '27639957': s.PLUS_PHOTO, + '35825640': Field() # not supporting maps yet +}) + s.MESSAGE_ATTACHMENT = Message([ 'embed_item', Message([ - # 249 (PLUS_PHOTO), 340, 335, 0 - 'type_', RepeatedField(Field()) - 'data', Field() # can be a dict + 'type', RepeatedField(EnumField(s.ItemType)) + 'data', s.EMBED_ITEM # this is a dictionary, which is like an ordinary object that has members that need to be looked up using a tag number ]) ]) @@ -469,6 +515,8 @@ s.CLIENT_GET_ENTITY_BY_ID_RESPONSE = Message([ None, Field() # 'cgebirp' 'response_header', s.CLIENT_RESPONSE_HEADER 'entities', RepeatedField(s.CLIENT_ENTITY) + 'unknown', RepeatedField(Message([None, Field() + 'entities', RepeatedField(s.CLIENT_ENTITY)])) ]) module.exports = s diff --git a/test/test-initparse.coffee b/test/test-initparse.coffee old mode 100644 new mode 100755 index 6a9d6bd..cf0329c --- a/test/test-initparse.coffee +++ b/test/test-initparse.coffee @@ -3,7 +3,8 @@ deql = assert.deepEqual {CLIENT_GET_SELF_INFO_RESPONSE INITIAL_CLIENT_ENTITIES, -CLIENT_CONVERSATION_STATE_LIST} = require '../src/schema' +CLIENT_CONVERSATION_STATE_LIST, +EMBED_ITEM} = require '../src/schema' msg1 = ["cgsirp",[1,null,"","1950326504872917925",1430493729941000],[null,null,null,null,null,null,null,[1,0,[],null,null,null,null,null,[[]]],["102224360723365489932","102224360723365489932"],[1,"Bo Tenström","Bo","//lh5.googleusercontent.com/-99B0CMsSo68/AAAAAAAAAAI/AAAAAAAAABI/v8oOeHFwNSI/photo.jpg",["botenstrom2@gmail.com"],[],null,null,null,null,null,2,[],[]],null,null,2,null,0,0,0],0,[],[0,null,0],[0],[[],[],2],[[8,0],[9,1],[22,0],[19,1],[10,1],[11,1],[14,0],[20,0],[17,0],[16,0],[23,0],[24,0],[27,0],[5,1],[6,1],[1,0],[2,1],[7,1],[3,1],[4,1],[29,1],[13,0],[12,0],[15,0],[28,0]],[1],1,[1,1],[null,[],[[5,0],[4,0],[2,0],[6,1],[1,0],[3,1]]],1,1,0,2,[],1,["SE",46],[],null,[1]] @@ -33,3 +34,12 @@ describe 'CLIENT_CONVERSATION_STATE_LIST', -> it 'parses', -> deql CLIENT_CONVERSATION_STATE_LIST.parse(msg3), cmp3 + +msg4 = { "27639957": [["https://plus.google.com/photos/albums/p16geqve3h5t3tqdn4odhtha2j5lqkale?pid=6275042227379600450&oid=103730981268153889186", null, null, "https://lh3.googleusercontent.com/-QUwpEWamKew/VxVtqMGJfEI/AAAAAAAAAFM/jeRZI6e_DUIZkUVdhXoNbQNiY8UxBGvwwCK8B/s0/2016-04-18.jpg", null, null, null, null, null, 768, 401], "103730981268153889186", "6272415246136908337", "6275042227379600450", null, "https://lh3.googleusercontent.com/-QUwpEWamKew/VxVtqMGJfEI/AAAAAAAAAFM/jeRZI6e_DUIZkUVdhXoNbQNiY8UxBGvwwCK8B/s0/2016-04-18.jpg", null, null, null, "https://lh3.googleusercontent.com/nUIH-qp7Cgeei1PAAdirnxrtS2Ryc6A2Tai2gzOdR0oIAPxhIj9BtSkTkYQWxalPvr4", null, null, 1, ["shared_group_6275042227379600450", "BABEL_STREAM_ID", "BABEL_UNIQUE_ID_1e30efc4-8f46-4f58-ab52-c2b8ec77a3a7"]] } + +cmp4 = {"27639957":{"album_id":"6272415246136908337","media_type":"MEDIA_TYPE_PHOTO","original_content_url":"https://lh3.googleusercontent.com/nUIH-qp7Cgeei1PAAdirnxrtS2Ryc6A2Tai2gzOdR0oIAPxhIj9BtSkTkYQWxalPvr4","owner_obfuscated_id":"103730981268153889186","photo_id":"6275042227379600450","stream_id":["shared_group_6275042227379600450","BABEL_STREAM_ID","BABEL_UNIQUE_ID_1e30efc4-8f46-4f58-ab52-c2b8ec77a3a7"],"thumbnail":{"height_px":401,"image_url":"https://lh3.googleusercontent.com/-QUwpEWamKew/VxVtqMGJfEI/AAAAAAAAAFM/jeRZI6e_DUIZkUVdhXoNbQNiY8UxBGvwwCK8B/s0/2016-04-18.jpg","url":"https://plus.google.com/photos/albums/p16geqve3h5t3tqdn4odhtha2j5lqkale?pid=6275042227379600450&oid=103730981268153889186","width_px":768},"url":"https://lh3.googleusercontent.com/-QUwpEWamKew/VxVtqMGJfEI/AAAAAAAAAFM/jeRZI6e_DUIZkUVdhXoNbQNiY8UxBGvwwCK8B/s0/2016-04-18.jpg"}} + +describe 'EMBED_ITEM', -> + + it 'parses', -> + deql EMBED_ITEM.parse(msg4), cmp4