├── README.md ├── akad ├── AccountSupervisorService.py ├── AgeCheckService.py ├── AuthService.py ├── BotService.py ├── BuddyManagementService.py ├── BuddyService.py ├── CallService.py ├── ChannelApplicationProvidedService.py ├── ChannelService.py ├── LiffService.py ├── LongpollingService.py ├── MessageService.py ├── ShopService.py ├── SnsAdaptorService.py ├── SpotService.py ├── SquareService.py ├── TalkService.py ├── TicketService.py ├── UniversalNotificationService.py ├── __init__.py ├── constants.py └── ttypes.py ├── linepy ├── __init__.py ├── auth.py ├── call.py ├── callback.py ├── channel.py ├── client.py ├── config.py ├── e2ee.py ├── liff.py ├── login.py ├── models.py ├── object.py ├── oepoll.py ├── server.py ├── session.py ├── shop.py ├── square.py ├── talk.py ├── timeline.py └── transport.py └── requirements.txt /README.md: -------------------------------------------------------------------------------- 1 | # LINEPY 2 | LINEPY but QrCode login was fixed. 3 | 4 | Thanks to [Crash-override404](https://github.com/crash-override404/linepy-modified) for original LINEPY. 5 | -------------------------------------------------------------------------------- /akad/LiffService.py: -------------------------------------------------------------------------------- 1 | # 2 | # Autogenerated by Thrift Compiler (0.12.0) 3 | # 4 | # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 5 | # 6 | # options string: py 7 | # 8 | 9 | from thrift.Thrift import TType, TMessageType, TFrozenDict, TException, TApplicationException 10 | from thrift.protocol.TProtocol import TProtocolException 11 | from thrift.TRecursive import fix_spec 12 | 13 | import sys 14 | import logging 15 | from .ttypes import * 16 | from thrift.Thrift import TProcessor 17 | from thrift.transport import TTransport 18 | all_structs = [] 19 | 20 | 21 | class Iface(object): 22 | def issueLiffView(self, request): 23 | """ 24 | Parameters: 25 | - request 26 | 27 | """ 28 | pass 29 | 30 | def revokeToken(self, request): 31 | """ 32 | Parameters: 33 | - request 34 | 35 | """ 36 | pass 37 | 38 | 39 | class Client(Iface): 40 | def __init__(self, iprot, oprot=None): 41 | self._iprot = self._oprot = iprot 42 | if oprot is not None: 43 | self._oprot = oprot 44 | self._seqid = 0 45 | 46 | def issueLiffView(self, request): 47 | """ 48 | Parameters: 49 | - request 50 | 51 | """ 52 | self.send_issueLiffView(request) 53 | return self.recv_issueLiffView() 54 | 55 | def send_issueLiffView(self, request): 56 | self._oprot.writeMessageBegin('issueLiffView', TMessageType.CALL, self._seqid) 57 | args = issueLiffView_args() 58 | args.request = request 59 | args.write(self._oprot) 60 | self._oprot.writeMessageEnd() 61 | self._oprot.trans.flush() 62 | 63 | def recv_issueLiffView(self): 64 | iprot = self._iprot 65 | (fname, mtype, rseqid) = iprot.readMessageBegin() 66 | if mtype == TMessageType.EXCEPTION: 67 | x = TApplicationException() 68 | x.read(iprot) 69 | iprot.readMessageEnd() 70 | raise x 71 | result = issueLiffView_result() 72 | result.read(iprot) 73 | iprot.readMessageEnd() 74 | if result.success is not None: 75 | return result.success 76 | if result.e is not None: 77 | raise result.e 78 | raise TApplicationException(TApplicationException.MISSING_RESULT, "issueLiffView failed: unknown result") 79 | 80 | def revokeToken(self, request): 81 | """ 82 | Parameters: 83 | - request 84 | 85 | """ 86 | self.send_revokeToken(request) 87 | self.recv_revokeToken() 88 | 89 | def send_revokeToken(self, request): 90 | self._oprot.writeMessageBegin('revokeToken', TMessageType.CALL, self._seqid) 91 | args = revokeToken_args() 92 | args.request = request 93 | args.write(self._oprot) 94 | self._oprot.writeMessageEnd() 95 | self._oprot.trans.flush() 96 | 97 | def recv_revokeToken(self): 98 | iprot = self._iprot 99 | (fname, mtype, rseqid) = iprot.readMessageBegin() 100 | if mtype == TMessageType.EXCEPTION: 101 | x = TApplicationException() 102 | x.read(iprot) 103 | iprot.readMessageEnd() 104 | raise x 105 | result = revokeToken_result() 106 | result.read(iprot) 107 | iprot.readMessageEnd() 108 | if result.e is not None: 109 | raise result.e 110 | return 111 | 112 | 113 | class Processor(Iface, TProcessor): 114 | def __init__(self, handler): 115 | self._handler = handler 116 | self._processMap = {} 117 | self._processMap["issueLiffView"] = Processor.process_issueLiffView 118 | self._processMap["revokeToken"] = Processor.process_revokeToken 119 | 120 | def process(self, iprot, oprot): 121 | (name, type, seqid) = iprot.readMessageBegin() 122 | if name not in self._processMap: 123 | iprot.skip(TType.STRUCT) 124 | iprot.readMessageEnd() 125 | x = TApplicationException(TApplicationException.UNKNOWN_METHOD, 'Unknown function %s' % (name)) 126 | oprot.writeMessageBegin(name, TMessageType.EXCEPTION, seqid) 127 | x.write(oprot) 128 | oprot.writeMessageEnd() 129 | oprot.trans.flush() 130 | return 131 | else: 132 | self._processMap[name](self, seqid, iprot, oprot) 133 | return True 134 | 135 | def process_issueLiffView(self, seqid, iprot, oprot): 136 | args = issueLiffView_args() 137 | args.read(iprot) 138 | iprot.readMessageEnd() 139 | result = issueLiffView_result() 140 | try: 141 | result.success = self._handler.issueLiffView(args.request) 142 | msg_type = TMessageType.REPLY 143 | except TTransport.TTransportException: 144 | raise 145 | except LiffException as e: 146 | msg_type = TMessageType.REPLY 147 | result.e = e 148 | except TApplicationException as ex: 149 | logging.exception('TApplication exception in handler') 150 | msg_type = TMessageType.EXCEPTION 151 | result = ex 152 | except Exception: 153 | logging.exception('Unexpected exception in handler') 154 | msg_type = TMessageType.EXCEPTION 155 | result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') 156 | oprot.writeMessageBegin("issueLiffView", msg_type, seqid) 157 | result.write(oprot) 158 | oprot.writeMessageEnd() 159 | oprot.trans.flush() 160 | 161 | def process_revokeToken(self, seqid, iprot, oprot): 162 | args = revokeToken_args() 163 | args.read(iprot) 164 | iprot.readMessageEnd() 165 | result = revokeToken_result() 166 | try: 167 | self._handler.revokeToken(args.request) 168 | msg_type = TMessageType.REPLY 169 | except TTransport.TTransportException: 170 | raise 171 | except LiffException as e: 172 | msg_type = TMessageType.REPLY 173 | result.e = e 174 | except TApplicationException as ex: 175 | logging.exception('TApplication exception in handler') 176 | msg_type = TMessageType.EXCEPTION 177 | result = ex 178 | except Exception: 179 | logging.exception('Unexpected exception in handler') 180 | msg_type = TMessageType.EXCEPTION 181 | result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') 182 | oprot.writeMessageBegin("revokeToken", msg_type, seqid) 183 | result.write(oprot) 184 | oprot.writeMessageEnd() 185 | oprot.trans.flush() 186 | 187 | # HELPER FUNCTIONS AND STRUCTURES 188 | 189 | 190 | class issueLiffView_args(object): 191 | """ 192 | Attributes: 193 | - request 194 | 195 | """ 196 | 197 | 198 | def __init__(self, request=None,): 199 | self.request = request 200 | 201 | def read(self, iprot): 202 | if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: 203 | iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) 204 | return 205 | iprot.readStructBegin() 206 | while True: 207 | (fname, ftype, fid) = iprot.readFieldBegin() 208 | if ftype == TType.STOP: 209 | break 210 | if fid == 1: 211 | if ftype == TType.STRUCT: 212 | self.request = LiffViewRequest() 213 | self.request.read(iprot) 214 | else: 215 | iprot.skip(ftype) 216 | else: 217 | iprot.skip(ftype) 218 | iprot.readFieldEnd() 219 | iprot.readStructEnd() 220 | 221 | def write(self, oprot): 222 | if oprot._fast_encode is not None and self.thrift_spec is not None: 223 | oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) 224 | return 225 | oprot.writeStructBegin('issueLiffView_args') 226 | if self.request is not None: 227 | oprot.writeFieldBegin('request', TType.STRUCT, 1) 228 | self.request.write(oprot) 229 | oprot.writeFieldEnd() 230 | oprot.writeFieldStop() 231 | oprot.writeStructEnd() 232 | 233 | def validate(self): 234 | return 235 | 236 | def __repr__(self): 237 | L = ['%s=%r' % (key, value) 238 | for key, value in self.__dict__.items()] 239 | return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) 240 | 241 | def __eq__(self, other): 242 | return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ 243 | 244 | def __ne__(self, other): 245 | return not (self == other) 246 | all_structs.append(issueLiffView_args) 247 | issueLiffView_args.thrift_spec = ( 248 | None, # 0 249 | (1, TType.STRUCT, 'request', [LiffViewRequest, None], None, ), # 1 250 | ) 251 | 252 | 253 | class issueLiffView_result(object): 254 | """ 255 | Attributes: 256 | - success 257 | - e 258 | 259 | """ 260 | 261 | 262 | def __init__(self, success=None, e=None,): 263 | self.success = success 264 | self.e = e 265 | 266 | def read(self, iprot): 267 | if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: 268 | iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) 269 | return 270 | iprot.readStructBegin() 271 | while True: 272 | (fname, ftype, fid) = iprot.readFieldBegin() 273 | if ftype == TType.STOP: 274 | break 275 | if fid == 0: 276 | if ftype == TType.STRUCT: 277 | self.success = LiffViewResponse() 278 | self.success.read(iprot) 279 | else: 280 | iprot.skip(ftype) 281 | elif fid == 1: 282 | if ftype == TType.STRUCT: 283 | self.e = LiffException() 284 | self.e.read(iprot) 285 | else: 286 | iprot.skip(ftype) 287 | else: 288 | iprot.skip(ftype) 289 | iprot.readFieldEnd() 290 | iprot.readStructEnd() 291 | 292 | def write(self, oprot): 293 | if oprot._fast_encode is not None and self.thrift_spec is not None: 294 | oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) 295 | return 296 | oprot.writeStructBegin('issueLiffView_result') 297 | if self.success is not None: 298 | oprot.writeFieldBegin('success', TType.STRUCT, 0) 299 | self.success.write(oprot) 300 | oprot.writeFieldEnd() 301 | if self.e is not None: 302 | oprot.writeFieldBegin('e', TType.STRUCT, 1) 303 | self.e.write(oprot) 304 | oprot.writeFieldEnd() 305 | oprot.writeFieldStop() 306 | oprot.writeStructEnd() 307 | 308 | def validate(self): 309 | return 310 | 311 | def __repr__(self): 312 | L = ['%s=%r' % (key, value) 313 | for key, value in self.__dict__.items()] 314 | return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) 315 | 316 | def __eq__(self, other): 317 | return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ 318 | 319 | def __ne__(self, other): 320 | return not (self == other) 321 | all_structs.append(issueLiffView_result) 322 | issueLiffView_result.thrift_spec = ( 323 | (0, TType.STRUCT, 'success', [LiffViewResponse, None], None, ), # 0 324 | (1, TType.STRUCT, 'e', [LiffException, None], None, ), # 1 325 | ) 326 | 327 | 328 | class revokeToken_args(object): 329 | """ 330 | Attributes: 331 | - request 332 | 333 | """ 334 | 335 | 336 | def __init__(self, request=None,): 337 | self.request = request 338 | 339 | def read(self, iprot): 340 | if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: 341 | iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) 342 | return 343 | iprot.readStructBegin() 344 | while True: 345 | (fname, ftype, fid) = iprot.readFieldBegin() 346 | if ftype == TType.STOP: 347 | break 348 | if fid == 1: 349 | if ftype == TType.STRUCT: 350 | self.request = RevokeTokenRequest() 351 | self.request.read(iprot) 352 | else: 353 | iprot.skip(ftype) 354 | else: 355 | iprot.skip(ftype) 356 | iprot.readFieldEnd() 357 | iprot.readStructEnd() 358 | 359 | def write(self, oprot): 360 | if oprot._fast_encode is not None and self.thrift_spec is not None: 361 | oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) 362 | return 363 | oprot.writeStructBegin('revokeToken_args') 364 | if self.request is not None: 365 | oprot.writeFieldBegin('request', TType.STRUCT, 1) 366 | self.request.write(oprot) 367 | oprot.writeFieldEnd() 368 | oprot.writeFieldStop() 369 | oprot.writeStructEnd() 370 | 371 | def validate(self): 372 | return 373 | 374 | def __repr__(self): 375 | L = ['%s=%r' % (key, value) 376 | for key, value in self.__dict__.items()] 377 | return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) 378 | 379 | def __eq__(self, other): 380 | return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ 381 | 382 | def __ne__(self, other): 383 | return not (self == other) 384 | all_structs.append(revokeToken_args) 385 | revokeToken_args.thrift_spec = ( 386 | None, # 0 387 | (1, TType.STRUCT, 'request', [RevokeTokenRequest, None], None, ), # 1 388 | ) 389 | 390 | 391 | class revokeToken_result(object): 392 | """ 393 | Attributes: 394 | - e 395 | 396 | """ 397 | 398 | 399 | def __init__(self, e=None,): 400 | self.e = e 401 | 402 | def read(self, iprot): 403 | if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: 404 | iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) 405 | return 406 | iprot.readStructBegin() 407 | while True: 408 | (fname, ftype, fid) = iprot.readFieldBegin() 409 | if ftype == TType.STOP: 410 | break 411 | if fid == 1: 412 | if ftype == TType.STRUCT: 413 | self.e = LiffException() 414 | self.e.read(iprot) 415 | else: 416 | iprot.skip(ftype) 417 | else: 418 | iprot.skip(ftype) 419 | iprot.readFieldEnd() 420 | iprot.readStructEnd() 421 | 422 | def write(self, oprot): 423 | if oprot._fast_encode is not None and self.thrift_spec is not None: 424 | oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) 425 | return 426 | oprot.writeStructBegin('revokeToken_result') 427 | if self.e is not None: 428 | oprot.writeFieldBegin('e', TType.STRUCT, 1) 429 | self.e.write(oprot) 430 | oprot.writeFieldEnd() 431 | oprot.writeFieldStop() 432 | oprot.writeStructEnd() 433 | 434 | def validate(self): 435 | return 436 | 437 | def __repr__(self): 438 | L = ['%s=%r' % (key, value) 439 | for key, value in self.__dict__.items()] 440 | return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) 441 | 442 | def __eq__(self, other): 443 | return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ 444 | 445 | def __ne__(self, other): 446 | return not (self == other) 447 | all_structs.append(revokeToken_result) 448 | revokeToken_result.thrift_spec = ( 449 | None, # 0 450 | (1, TType.STRUCT, 'e', [LiffException, None], None, ), # 1 451 | ) 452 | fix_spec(all_structs) 453 | del all_structs 454 | 455 | -------------------------------------------------------------------------------- /akad/LongpollingService.py: -------------------------------------------------------------------------------- 1 | # 2 | # Autogenerated by Thrift Compiler (0.12.0) 3 | # 4 | # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 5 | # 6 | # options string: py 7 | # 8 | 9 | from thrift.Thrift import TType, TMessageType, TFrozenDict, TException, TApplicationException 10 | from thrift.protocol.TProtocol import TProtocolException 11 | from thrift.TRecursive import fix_spec 12 | 13 | import sys 14 | import logging 15 | from .ttypes import * 16 | from thrift.Thrift import TProcessor 17 | from thrift.transport import TTransport 18 | all_structs = [] 19 | 20 | 21 | class Iface(object): 22 | def wakeUpLongPolling(self, wakeUpLongPolling): 23 | """ 24 | Parameters: 25 | - wakeUpLongPolling 26 | 27 | """ 28 | pass 29 | 30 | 31 | class Client(Iface): 32 | def __init__(self, iprot, oprot=None): 33 | self._iprot = self._oprot = iprot 34 | if oprot is not None: 35 | self._oprot = oprot 36 | self._seqid = 0 37 | 38 | def wakeUpLongPolling(self, wakeUpLongPolling): 39 | """ 40 | Parameters: 41 | - wakeUpLongPolling 42 | 43 | """ 44 | self.send_wakeUpLongPolling(wakeUpLongPolling) 45 | return self.recv_wakeUpLongPolling() 46 | 47 | def send_wakeUpLongPolling(self, wakeUpLongPolling): 48 | self._oprot.writeMessageBegin('wakeUpLongPolling', TMessageType.CALL, self._seqid) 49 | args = wakeUpLongPolling_args() 50 | args.wakeUpLongPolling = wakeUpLongPolling 51 | args.write(self._oprot) 52 | self._oprot.writeMessageEnd() 53 | self._oprot.trans.flush() 54 | 55 | def recv_wakeUpLongPolling(self): 56 | iprot = self._iprot 57 | (fname, mtype, rseqid) = iprot.readMessageBegin() 58 | if mtype == TMessageType.EXCEPTION: 59 | x = TApplicationException() 60 | x.read(iprot) 61 | iprot.readMessageEnd() 62 | raise x 63 | result = wakeUpLongPolling_result() 64 | result.read(iprot) 65 | iprot.readMessageEnd() 66 | if result.success is not None: 67 | return result.success 68 | if result.e is not None: 69 | raise result.e 70 | raise TApplicationException(TApplicationException.MISSING_RESULT, "wakeUpLongPolling failed: unknown result") 71 | 72 | 73 | class Processor(Iface, TProcessor): 74 | def __init__(self, handler): 75 | self._handler = handler 76 | self._processMap = {} 77 | self._processMap["wakeUpLongPolling"] = Processor.process_wakeUpLongPolling 78 | 79 | def process(self, iprot, oprot): 80 | (name, type, seqid) = iprot.readMessageBegin() 81 | if name not in self._processMap: 82 | iprot.skip(TType.STRUCT) 83 | iprot.readMessageEnd() 84 | x = TApplicationException(TApplicationException.UNKNOWN_METHOD, 'Unknown function %s' % (name)) 85 | oprot.writeMessageBegin(name, TMessageType.EXCEPTION, seqid) 86 | x.write(oprot) 87 | oprot.writeMessageEnd() 88 | oprot.trans.flush() 89 | return 90 | else: 91 | self._processMap[name](self, seqid, iprot, oprot) 92 | return True 93 | 94 | def process_wakeUpLongPolling(self, seqid, iprot, oprot): 95 | args = wakeUpLongPolling_args() 96 | args.read(iprot) 97 | iprot.readMessageEnd() 98 | result = wakeUpLongPolling_result() 99 | try: 100 | result.success = self._handler.wakeUpLongPolling(args.wakeUpLongPolling) 101 | msg_type = TMessageType.REPLY 102 | except TTransport.TTransportException: 103 | raise 104 | except TalkException as e: 105 | msg_type = TMessageType.REPLY 106 | result.e = e 107 | except TApplicationException as ex: 108 | logging.exception('TApplication exception in handler') 109 | msg_type = TMessageType.EXCEPTION 110 | result = ex 111 | except Exception: 112 | logging.exception('Unexpected exception in handler') 113 | msg_type = TMessageType.EXCEPTION 114 | result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') 115 | oprot.writeMessageBegin("wakeUpLongPolling", msg_type, seqid) 116 | result.write(oprot) 117 | oprot.writeMessageEnd() 118 | oprot.trans.flush() 119 | 120 | # HELPER FUNCTIONS AND STRUCTURES 121 | 122 | 123 | class wakeUpLongPolling_args(object): 124 | """ 125 | Attributes: 126 | - wakeUpLongPolling 127 | 128 | """ 129 | 130 | 131 | def __init__(self, wakeUpLongPolling=None,): 132 | self.wakeUpLongPolling = wakeUpLongPolling 133 | 134 | def read(self, iprot): 135 | if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: 136 | iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) 137 | return 138 | iprot.readStructBegin() 139 | while True: 140 | (fname, ftype, fid) = iprot.readFieldBegin() 141 | if ftype == TType.STOP: 142 | break 143 | if fid == 2: 144 | if ftype == TType.I64: 145 | self.wakeUpLongPolling = iprot.readI64() 146 | else: 147 | iprot.skip(ftype) 148 | else: 149 | iprot.skip(ftype) 150 | iprot.readFieldEnd() 151 | iprot.readStructEnd() 152 | 153 | def write(self, oprot): 154 | if oprot._fast_encode is not None and self.thrift_spec is not None: 155 | oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) 156 | return 157 | oprot.writeStructBegin('wakeUpLongPolling_args') 158 | if self.wakeUpLongPolling is not None: 159 | oprot.writeFieldBegin('wakeUpLongPolling', TType.I64, 2) 160 | oprot.writeI64(self.wakeUpLongPolling) 161 | oprot.writeFieldEnd() 162 | oprot.writeFieldStop() 163 | oprot.writeStructEnd() 164 | 165 | def validate(self): 166 | return 167 | 168 | def __repr__(self): 169 | L = ['%s=%r' % (key, value) 170 | for key, value in self.__dict__.items()] 171 | return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) 172 | 173 | def __eq__(self, other): 174 | return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ 175 | 176 | def __ne__(self, other): 177 | return not (self == other) 178 | all_structs.append(wakeUpLongPolling_args) 179 | wakeUpLongPolling_args.thrift_spec = ( 180 | None, # 0 181 | None, # 1 182 | (2, TType.I64, 'wakeUpLongPolling', None, None, ), # 2 183 | ) 184 | 185 | 186 | class wakeUpLongPolling_result(object): 187 | """ 188 | Attributes: 189 | - success 190 | - e 191 | 192 | """ 193 | 194 | 195 | def __init__(self, success=None, e=None,): 196 | self.success = success 197 | self.e = e 198 | 199 | def read(self, iprot): 200 | if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: 201 | iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) 202 | return 203 | iprot.readStructBegin() 204 | while True: 205 | (fname, ftype, fid) = iprot.readFieldBegin() 206 | if ftype == TType.STOP: 207 | break 208 | if fid == 0: 209 | if ftype == TType.BOOL: 210 | self.success = iprot.readBool() 211 | else: 212 | iprot.skip(ftype) 213 | elif fid == 1: 214 | if ftype == TType.STRUCT: 215 | self.e = TalkException() 216 | self.e.read(iprot) 217 | else: 218 | iprot.skip(ftype) 219 | else: 220 | iprot.skip(ftype) 221 | iprot.readFieldEnd() 222 | iprot.readStructEnd() 223 | 224 | def write(self, oprot): 225 | if oprot._fast_encode is not None and self.thrift_spec is not None: 226 | oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) 227 | return 228 | oprot.writeStructBegin('wakeUpLongPolling_result') 229 | if self.success is not None: 230 | oprot.writeFieldBegin('success', TType.BOOL, 0) 231 | oprot.writeBool(self.success) 232 | oprot.writeFieldEnd() 233 | if self.e is not None: 234 | oprot.writeFieldBegin('e', TType.STRUCT, 1) 235 | self.e.write(oprot) 236 | oprot.writeFieldEnd() 237 | oprot.writeFieldStop() 238 | oprot.writeStructEnd() 239 | 240 | def validate(self): 241 | return 242 | 243 | def __repr__(self): 244 | L = ['%s=%r' % (key, value) 245 | for key, value in self.__dict__.items()] 246 | return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) 247 | 248 | def __eq__(self, other): 249 | return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ 250 | 251 | def __ne__(self, other): 252 | return not (self == other) 253 | all_structs.append(wakeUpLongPolling_result) 254 | wakeUpLongPolling_result.thrift_spec = ( 255 | (0, TType.BOOL, 'success', None, None, ), # 0 256 | (1, TType.STRUCT, 'e', [TalkException, None], None, ), # 1 257 | ) 258 | fix_spec(all_structs) 259 | del all_structs 260 | 261 | -------------------------------------------------------------------------------- /akad/MessageService.py: -------------------------------------------------------------------------------- 1 | # 2 | # Autogenerated by Thrift Compiler (0.12.0) 3 | # 4 | # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 5 | # 6 | # options string: py 7 | # 8 | 9 | from thrift.Thrift import TType, TMessageType, TFrozenDict, TException, TApplicationException 10 | from thrift.protocol.TProtocol import TProtocolException 11 | from thrift.TRecursive import fix_spec 12 | 13 | import sys 14 | import logging 15 | from .ttypes import * 16 | from thrift.Thrift import TProcessor 17 | from thrift.transport import TTransport 18 | all_structs = [] 19 | 20 | 21 | class Iface(object): 22 | def fetchMessageOperations(self, localRevision, lastOpTimestamp, count): 23 | """ 24 | Parameters: 25 | - localRevision 26 | - lastOpTimestamp 27 | - count 28 | 29 | """ 30 | pass 31 | 32 | def getLastReadMessageIds(self, chatId): 33 | """ 34 | Parameters: 35 | - chatId 36 | 37 | """ 38 | pass 39 | 40 | def multiGetLastReadMessageIds(self, chatIds): 41 | """ 42 | Parameters: 43 | - chatIds 44 | 45 | """ 46 | pass 47 | 48 | 49 | class Client(Iface): 50 | def __init__(self, iprot, oprot=None): 51 | self._iprot = self._oprot = iprot 52 | if oprot is not None: 53 | self._oprot = oprot 54 | self._seqid = 0 55 | 56 | def fetchMessageOperations(self, localRevision, lastOpTimestamp, count): 57 | """ 58 | Parameters: 59 | - localRevision 60 | - lastOpTimestamp 61 | - count 62 | 63 | """ 64 | self.send_fetchMessageOperations(localRevision, lastOpTimestamp, count) 65 | return self.recv_fetchMessageOperations() 66 | 67 | def send_fetchMessageOperations(self, localRevision, lastOpTimestamp, count): 68 | self._oprot.writeMessageBegin('fetchMessageOperations', TMessageType.CALL, self._seqid) 69 | args = fetchMessageOperations_args() 70 | args.localRevision = localRevision 71 | args.lastOpTimestamp = lastOpTimestamp 72 | args.count = count 73 | args.write(self._oprot) 74 | self._oprot.writeMessageEnd() 75 | self._oprot.trans.flush() 76 | 77 | def recv_fetchMessageOperations(self): 78 | iprot = self._iprot 79 | (fname, mtype, rseqid) = iprot.readMessageBegin() 80 | if mtype == TMessageType.EXCEPTION: 81 | x = TApplicationException() 82 | x.read(iprot) 83 | iprot.readMessageEnd() 84 | raise x 85 | result = fetchMessageOperations_result() 86 | result.read(iprot) 87 | iprot.readMessageEnd() 88 | if result.success is not None: 89 | return result.success 90 | if result.e is not None: 91 | raise result.e 92 | raise TApplicationException(TApplicationException.MISSING_RESULT, "fetchMessageOperations failed: unknown result") 93 | 94 | def getLastReadMessageIds(self, chatId): 95 | """ 96 | Parameters: 97 | - chatId 98 | 99 | """ 100 | self.send_getLastReadMessageIds(chatId) 101 | return self.recv_getLastReadMessageIds() 102 | 103 | def send_getLastReadMessageIds(self, chatId): 104 | self._oprot.writeMessageBegin('getLastReadMessageIds', TMessageType.CALL, self._seqid) 105 | args = getLastReadMessageIds_args() 106 | args.chatId = chatId 107 | args.write(self._oprot) 108 | self._oprot.writeMessageEnd() 109 | self._oprot.trans.flush() 110 | 111 | def recv_getLastReadMessageIds(self): 112 | iprot = self._iprot 113 | (fname, mtype, rseqid) = iprot.readMessageBegin() 114 | if mtype == TMessageType.EXCEPTION: 115 | x = TApplicationException() 116 | x.read(iprot) 117 | iprot.readMessageEnd() 118 | raise x 119 | result = getLastReadMessageIds_result() 120 | result.read(iprot) 121 | iprot.readMessageEnd() 122 | if result.success is not None: 123 | return result.success 124 | if result.e is not None: 125 | raise result.e 126 | raise TApplicationException(TApplicationException.MISSING_RESULT, "getLastReadMessageIds failed: unknown result") 127 | 128 | def multiGetLastReadMessageIds(self, chatIds): 129 | """ 130 | Parameters: 131 | - chatIds 132 | 133 | """ 134 | self.send_multiGetLastReadMessageIds(chatIds) 135 | return self.recv_multiGetLastReadMessageIds() 136 | 137 | def send_multiGetLastReadMessageIds(self, chatIds): 138 | self._oprot.writeMessageBegin('multiGetLastReadMessageIds', TMessageType.CALL, self._seqid) 139 | args = multiGetLastReadMessageIds_args() 140 | args.chatIds = chatIds 141 | args.write(self._oprot) 142 | self._oprot.writeMessageEnd() 143 | self._oprot.trans.flush() 144 | 145 | def recv_multiGetLastReadMessageIds(self): 146 | iprot = self._iprot 147 | (fname, mtype, rseqid) = iprot.readMessageBegin() 148 | if mtype == TMessageType.EXCEPTION: 149 | x = TApplicationException() 150 | x.read(iprot) 151 | iprot.readMessageEnd() 152 | raise x 153 | result = multiGetLastReadMessageIds_result() 154 | result.read(iprot) 155 | iprot.readMessageEnd() 156 | if result.success is not None: 157 | return result.success 158 | if result.e is not None: 159 | raise result.e 160 | raise TApplicationException(TApplicationException.MISSING_RESULT, "multiGetLastReadMessageIds failed: unknown result") 161 | 162 | 163 | class Processor(Iface, TProcessor): 164 | def __init__(self, handler): 165 | self._handler = handler 166 | self._processMap = {} 167 | self._processMap["fetchMessageOperations"] = Processor.process_fetchMessageOperations 168 | self._processMap["getLastReadMessageIds"] = Processor.process_getLastReadMessageIds 169 | self._processMap["multiGetLastReadMessageIds"] = Processor.process_multiGetLastReadMessageIds 170 | 171 | def process(self, iprot, oprot): 172 | (name, type, seqid) = iprot.readMessageBegin() 173 | if name not in self._processMap: 174 | iprot.skip(TType.STRUCT) 175 | iprot.readMessageEnd() 176 | x = TApplicationException(TApplicationException.UNKNOWN_METHOD, 'Unknown function %s' % (name)) 177 | oprot.writeMessageBegin(name, TMessageType.EXCEPTION, seqid) 178 | x.write(oprot) 179 | oprot.writeMessageEnd() 180 | oprot.trans.flush() 181 | return 182 | else: 183 | self._processMap[name](self, seqid, iprot, oprot) 184 | return True 185 | 186 | def process_fetchMessageOperations(self, seqid, iprot, oprot): 187 | args = fetchMessageOperations_args() 188 | args.read(iprot) 189 | iprot.readMessageEnd() 190 | result = fetchMessageOperations_result() 191 | try: 192 | result.success = self._handler.fetchMessageOperations(args.localRevision, args.lastOpTimestamp, args.count) 193 | msg_type = TMessageType.REPLY 194 | except TTransport.TTransportException: 195 | raise 196 | except TalkException as e: 197 | msg_type = TMessageType.REPLY 198 | result.e = e 199 | except TApplicationException as ex: 200 | logging.exception('TApplication exception in handler') 201 | msg_type = TMessageType.EXCEPTION 202 | result = ex 203 | except Exception: 204 | logging.exception('Unexpected exception in handler') 205 | msg_type = TMessageType.EXCEPTION 206 | result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') 207 | oprot.writeMessageBegin("fetchMessageOperations", msg_type, seqid) 208 | result.write(oprot) 209 | oprot.writeMessageEnd() 210 | oprot.trans.flush() 211 | 212 | def process_getLastReadMessageIds(self, seqid, iprot, oprot): 213 | args = getLastReadMessageIds_args() 214 | args.read(iprot) 215 | iprot.readMessageEnd() 216 | result = getLastReadMessageIds_result() 217 | try: 218 | result.success = self._handler.getLastReadMessageIds(args.chatId) 219 | msg_type = TMessageType.REPLY 220 | except TTransport.TTransportException: 221 | raise 222 | except TalkException as e: 223 | msg_type = TMessageType.REPLY 224 | result.e = e 225 | except TApplicationException as ex: 226 | logging.exception('TApplication exception in handler') 227 | msg_type = TMessageType.EXCEPTION 228 | result = ex 229 | except Exception: 230 | logging.exception('Unexpected exception in handler') 231 | msg_type = TMessageType.EXCEPTION 232 | result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') 233 | oprot.writeMessageBegin("getLastReadMessageIds", msg_type, seqid) 234 | result.write(oprot) 235 | oprot.writeMessageEnd() 236 | oprot.trans.flush() 237 | 238 | def process_multiGetLastReadMessageIds(self, seqid, iprot, oprot): 239 | args = multiGetLastReadMessageIds_args() 240 | args.read(iprot) 241 | iprot.readMessageEnd() 242 | result = multiGetLastReadMessageIds_result() 243 | try: 244 | result.success = self._handler.multiGetLastReadMessageIds(args.chatIds) 245 | msg_type = TMessageType.REPLY 246 | except TTransport.TTransportException: 247 | raise 248 | except TalkException as e: 249 | msg_type = TMessageType.REPLY 250 | result.e = e 251 | except TApplicationException as ex: 252 | logging.exception('TApplication exception in handler') 253 | msg_type = TMessageType.EXCEPTION 254 | result = ex 255 | except Exception: 256 | logging.exception('Unexpected exception in handler') 257 | msg_type = TMessageType.EXCEPTION 258 | result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') 259 | oprot.writeMessageBegin("multiGetLastReadMessageIds", msg_type, seqid) 260 | result.write(oprot) 261 | oprot.writeMessageEnd() 262 | oprot.trans.flush() 263 | 264 | # HELPER FUNCTIONS AND STRUCTURES 265 | 266 | 267 | class fetchMessageOperations_args(object): 268 | """ 269 | Attributes: 270 | - localRevision 271 | - lastOpTimestamp 272 | - count 273 | 274 | """ 275 | 276 | 277 | def __init__(self, localRevision=None, lastOpTimestamp=None, count=None,): 278 | self.localRevision = localRevision 279 | self.lastOpTimestamp = lastOpTimestamp 280 | self.count = count 281 | 282 | def read(self, iprot): 283 | if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: 284 | iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) 285 | return 286 | iprot.readStructBegin() 287 | while True: 288 | (fname, ftype, fid) = iprot.readFieldBegin() 289 | if ftype == TType.STOP: 290 | break 291 | if fid == 2: 292 | if ftype == TType.I64: 293 | self.localRevision = iprot.readI64() 294 | else: 295 | iprot.skip(ftype) 296 | elif fid == 3: 297 | if ftype == TType.I64: 298 | self.lastOpTimestamp = iprot.readI64() 299 | else: 300 | iprot.skip(ftype) 301 | elif fid == 4: 302 | if ftype == TType.I32: 303 | self.count = iprot.readI32() 304 | else: 305 | iprot.skip(ftype) 306 | else: 307 | iprot.skip(ftype) 308 | iprot.readFieldEnd() 309 | iprot.readStructEnd() 310 | 311 | def write(self, oprot): 312 | if oprot._fast_encode is not None and self.thrift_spec is not None: 313 | oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) 314 | return 315 | oprot.writeStructBegin('fetchMessageOperations_args') 316 | if self.localRevision is not None: 317 | oprot.writeFieldBegin('localRevision', TType.I64, 2) 318 | oprot.writeI64(self.localRevision) 319 | oprot.writeFieldEnd() 320 | if self.lastOpTimestamp is not None: 321 | oprot.writeFieldBegin('lastOpTimestamp', TType.I64, 3) 322 | oprot.writeI64(self.lastOpTimestamp) 323 | oprot.writeFieldEnd() 324 | if self.count is not None: 325 | oprot.writeFieldBegin('count', TType.I32, 4) 326 | oprot.writeI32(self.count) 327 | oprot.writeFieldEnd() 328 | oprot.writeFieldStop() 329 | oprot.writeStructEnd() 330 | 331 | def validate(self): 332 | return 333 | 334 | def __repr__(self): 335 | L = ['%s=%r' % (key, value) 336 | for key, value in self.__dict__.items()] 337 | return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) 338 | 339 | def __eq__(self, other): 340 | return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ 341 | 342 | def __ne__(self, other): 343 | return not (self == other) 344 | all_structs.append(fetchMessageOperations_args) 345 | fetchMessageOperations_args.thrift_spec = ( 346 | None, # 0 347 | None, # 1 348 | (2, TType.I64, 'localRevision', None, None, ), # 2 349 | (3, TType.I64, 'lastOpTimestamp', None, None, ), # 3 350 | (4, TType.I32, 'count', None, None, ), # 4 351 | ) 352 | 353 | 354 | class fetchMessageOperations_result(object): 355 | """ 356 | Attributes: 357 | - success 358 | - e 359 | 360 | """ 361 | 362 | 363 | def __init__(self, success=None, e=None,): 364 | self.success = success 365 | self.e = e 366 | 367 | def read(self, iprot): 368 | if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: 369 | iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) 370 | return 371 | iprot.readStructBegin() 372 | while True: 373 | (fname, ftype, fid) = iprot.readFieldBegin() 374 | if ftype == TType.STOP: 375 | break 376 | if fid == 0: 377 | if ftype == TType.STRUCT: 378 | self.success = MessageOperations() 379 | self.success.read(iprot) 380 | else: 381 | iprot.skip(ftype) 382 | elif fid == 1: 383 | if ftype == TType.STRUCT: 384 | self.e = TalkException() 385 | self.e.read(iprot) 386 | else: 387 | iprot.skip(ftype) 388 | else: 389 | iprot.skip(ftype) 390 | iprot.readFieldEnd() 391 | iprot.readStructEnd() 392 | 393 | def write(self, oprot): 394 | if oprot._fast_encode is not None and self.thrift_spec is not None: 395 | oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) 396 | return 397 | oprot.writeStructBegin('fetchMessageOperations_result') 398 | if self.success is not None: 399 | oprot.writeFieldBegin('success', TType.STRUCT, 0) 400 | self.success.write(oprot) 401 | oprot.writeFieldEnd() 402 | if self.e is not None: 403 | oprot.writeFieldBegin('e', TType.STRUCT, 1) 404 | self.e.write(oprot) 405 | oprot.writeFieldEnd() 406 | oprot.writeFieldStop() 407 | oprot.writeStructEnd() 408 | 409 | def validate(self): 410 | return 411 | 412 | def __repr__(self): 413 | L = ['%s=%r' % (key, value) 414 | for key, value in self.__dict__.items()] 415 | return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) 416 | 417 | def __eq__(self, other): 418 | return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ 419 | 420 | def __ne__(self, other): 421 | return not (self == other) 422 | all_structs.append(fetchMessageOperations_result) 423 | fetchMessageOperations_result.thrift_spec = ( 424 | (0, TType.STRUCT, 'success', [MessageOperations, None], None, ), # 0 425 | (1, TType.STRUCT, 'e', [TalkException, None], None, ), # 1 426 | ) 427 | 428 | 429 | class getLastReadMessageIds_args(object): 430 | """ 431 | Attributes: 432 | - chatId 433 | 434 | """ 435 | 436 | 437 | def __init__(self, chatId=None,): 438 | self.chatId = chatId 439 | 440 | def read(self, iprot): 441 | if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: 442 | iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) 443 | return 444 | iprot.readStructBegin() 445 | while True: 446 | (fname, ftype, fid) = iprot.readFieldBegin() 447 | if ftype == TType.STOP: 448 | break 449 | if fid == 2: 450 | if ftype == TType.STRING: 451 | self.chatId = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() 452 | else: 453 | iprot.skip(ftype) 454 | else: 455 | iprot.skip(ftype) 456 | iprot.readFieldEnd() 457 | iprot.readStructEnd() 458 | 459 | def write(self, oprot): 460 | if oprot._fast_encode is not None and self.thrift_spec is not None: 461 | oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) 462 | return 463 | oprot.writeStructBegin('getLastReadMessageIds_args') 464 | if self.chatId is not None: 465 | oprot.writeFieldBegin('chatId', TType.STRING, 2) 466 | oprot.writeString(self.chatId.encode('utf-8') if sys.version_info[0] == 2 else self.chatId) 467 | oprot.writeFieldEnd() 468 | oprot.writeFieldStop() 469 | oprot.writeStructEnd() 470 | 471 | def validate(self): 472 | return 473 | 474 | def __repr__(self): 475 | L = ['%s=%r' % (key, value) 476 | for key, value in self.__dict__.items()] 477 | return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) 478 | 479 | def __eq__(self, other): 480 | return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ 481 | 482 | def __ne__(self, other): 483 | return not (self == other) 484 | all_structs.append(getLastReadMessageIds_args) 485 | getLastReadMessageIds_args.thrift_spec = ( 486 | None, # 0 487 | None, # 1 488 | (2, TType.STRING, 'chatId', 'UTF8', None, ), # 2 489 | ) 490 | 491 | 492 | class getLastReadMessageIds_result(object): 493 | """ 494 | Attributes: 495 | - success 496 | - e 497 | 498 | """ 499 | 500 | 501 | def __init__(self, success=None, e=None,): 502 | self.success = success 503 | self.e = e 504 | 505 | def read(self, iprot): 506 | if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: 507 | iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) 508 | return 509 | iprot.readStructBegin() 510 | while True: 511 | (fname, ftype, fid) = iprot.readFieldBegin() 512 | if ftype == TType.STOP: 513 | break 514 | if fid == 0: 515 | if ftype == TType.STRUCT: 516 | self.success = LastReadMessageIds() 517 | self.success.read(iprot) 518 | else: 519 | iprot.skip(ftype) 520 | elif fid == 1: 521 | if ftype == TType.STRUCT: 522 | self.e = TalkException() 523 | self.e.read(iprot) 524 | else: 525 | iprot.skip(ftype) 526 | else: 527 | iprot.skip(ftype) 528 | iprot.readFieldEnd() 529 | iprot.readStructEnd() 530 | 531 | def write(self, oprot): 532 | if oprot._fast_encode is not None and self.thrift_spec is not None: 533 | oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) 534 | return 535 | oprot.writeStructBegin('getLastReadMessageIds_result') 536 | if self.success is not None: 537 | oprot.writeFieldBegin('success', TType.STRUCT, 0) 538 | self.success.write(oprot) 539 | oprot.writeFieldEnd() 540 | if self.e is not None: 541 | oprot.writeFieldBegin('e', TType.STRUCT, 1) 542 | self.e.write(oprot) 543 | oprot.writeFieldEnd() 544 | oprot.writeFieldStop() 545 | oprot.writeStructEnd() 546 | 547 | def validate(self): 548 | return 549 | 550 | def __repr__(self): 551 | L = ['%s=%r' % (key, value) 552 | for key, value in self.__dict__.items()] 553 | return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) 554 | 555 | def __eq__(self, other): 556 | return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ 557 | 558 | def __ne__(self, other): 559 | return not (self == other) 560 | all_structs.append(getLastReadMessageIds_result) 561 | getLastReadMessageIds_result.thrift_spec = ( 562 | (0, TType.STRUCT, 'success', [LastReadMessageIds, None], None, ), # 0 563 | (1, TType.STRUCT, 'e', [TalkException, None], None, ), # 1 564 | ) 565 | 566 | 567 | class multiGetLastReadMessageIds_args(object): 568 | """ 569 | Attributes: 570 | - chatIds 571 | 572 | """ 573 | 574 | 575 | def __init__(self, chatIds=None,): 576 | self.chatIds = chatIds 577 | 578 | def read(self, iprot): 579 | if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: 580 | iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) 581 | return 582 | iprot.readStructBegin() 583 | while True: 584 | (fname, ftype, fid) = iprot.readFieldBegin() 585 | if ftype == TType.STOP: 586 | break 587 | if fid == 2: 588 | if ftype == TType.LIST: 589 | self.chatIds = [] 590 | (_etype1626, _size1623) = iprot.readListBegin() 591 | for _i1627 in range(_size1623): 592 | _elem1628 = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() 593 | self.chatIds.append(_elem1628) 594 | iprot.readListEnd() 595 | else: 596 | iprot.skip(ftype) 597 | else: 598 | iprot.skip(ftype) 599 | iprot.readFieldEnd() 600 | iprot.readStructEnd() 601 | 602 | def write(self, oprot): 603 | if oprot._fast_encode is not None and self.thrift_spec is not None: 604 | oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) 605 | return 606 | oprot.writeStructBegin('multiGetLastReadMessageIds_args') 607 | if self.chatIds is not None: 608 | oprot.writeFieldBegin('chatIds', TType.LIST, 2) 609 | oprot.writeListBegin(TType.STRING, len(self.chatIds)) 610 | for iter1629 in self.chatIds: 611 | oprot.writeString(iter1629.encode('utf-8') if sys.version_info[0] == 2 else iter1629) 612 | oprot.writeListEnd() 613 | oprot.writeFieldEnd() 614 | oprot.writeFieldStop() 615 | oprot.writeStructEnd() 616 | 617 | def validate(self): 618 | return 619 | 620 | def __repr__(self): 621 | L = ['%s=%r' % (key, value) 622 | for key, value in self.__dict__.items()] 623 | return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) 624 | 625 | def __eq__(self, other): 626 | return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ 627 | 628 | def __ne__(self, other): 629 | return not (self == other) 630 | all_structs.append(multiGetLastReadMessageIds_args) 631 | multiGetLastReadMessageIds_args.thrift_spec = ( 632 | None, # 0 633 | None, # 1 634 | (2, TType.LIST, 'chatIds', (TType.STRING, 'UTF8', False), None, ), # 2 635 | ) 636 | 637 | 638 | class multiGetLastReadMessageIds_result(object): 639 | """ 640 | Attributes: 641 | - success 642 | - e 643 | 644 | """ 645 | 646 | 647 | def __init__(self, success=None, e=None,): 648 | self.success = success 649 | self.e = e 650 | 651 | def read(self, iprot): 652 | if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: 653 | iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) 654 | return 655 | iprot.readStructBegin() 656 | while True: 657 | (fname, ftype, fid) = iprot.readFieldBegin() 658 | if ftype == TType.STOP: 659 | break 660 | if fid == 0: 661 | if ftype == TType.LIST: 662 | self.success = [] 663 | (_etype1633, _size1630) = iprot.readListBegin() 664 | for _i1634 in range(_size1630): 665 | _elem1635 = LastReadMessageIds() 666 | _elem1635.read(iprot) 667 | self.success.append(_elem1635) 668 | iprot.readListEnd() 669 | else: 670 | iprot.skip(ftype) 671 | elif fid == 1: 672 | if ftype == TType.STRUCT: 673 | self.e = TalkException() 674 | self.e.read(iprot) 675 | else: 676 | iprot.skip(ftype) 677 | else: 678 | iprot.skip(ftype) 679 | iprot.readFieldEnd() 680 | iprot.readStructEnd() 681 | 682 | def write(self, oprot): 683 | if oprot._fast_encode is not None and self.thrift_spec is not None: 684 | oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) 685 | return 686 | oprot.writeStructBegin('multiGetLastReadMessageIds_result') 687 | if self.success is not None: 688 | oprot.writeFieldBegin('success', TType.LIST, 0) 689 | oprot.writeListBegin(TType.STRUCT, len(self.success)) 690 | for iter1636 in self.success: 691 | iter1636.write(oprot) 692 | oprot.writeListEnd() 693 | oprot.writeFieldEnd() 694 | if self.e is not None: 695 | oprot.writeFieldBegin('e', TType.STRUCT, 1) 696 | self.e.write(oprot) 697 | oprot.writeFieldEnd() 698 | oprot.writeFieldStop() 699 | oprot.writeStructEnd() 700 | 701 | def validate(self): 702 | return 703 | 704 | def __repr__(self): 705 | L = ['%s=%r' % (key, value) 706 | for key, value in self.__dict__.items()] 707 | return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) 708 | 709 | def __eq__(self, other): 710 | return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ 711 | 712 | def __ne__(self, other): 713 | return not (self == other) 714 | all_structs.append(multiGetLastReadMessageIds_result) 715 | multiGetLastReadMessageIds_result.thrift_spec = ( 716 | (0, TType.LIST, 'success', (TType.STRUCT, [LastReadMessageIds, None], False), None, ), # 0 717 | (1, TType.STRUCT, 'e', [TalkException, None], None, ), # 1 718 | ) 719 | fix_spec(all_structs) 720 | del all_structs 721 | 722 | -------------------------------------------------------------------------------- /akad/SpotService.py: -------------------------------------------------------------------------------- 1 | # 2 | # Autogenerated by Thrift Compiler (0.12.0) 3 | # 4 | # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 5 | # 6 | # options string: py 7 | # 8 | 9 | from thrift.Thrift import TType, TMessageType, TFrozenDict, TException, TApplicationException 10 | from thrift.protocol.TProtocol import TProtocolException 11 | from thrift.TRecursive import fix_spec 12 | 13 | import sys 14 | import logging 15 | from .ttypes import * 16 | from thrift.Thrift import TProcessor 17 | from thrift.transport import TTransport 18 | all_structs = [] 19 | 20 | 21 | class Iface(object): 22 | def lookupByPhoneNumber(self, countryAreaCode, phoneNumber): 23 | """ 24 | Parameters: 25 | - countryAreaCode 26 | - phoneNumber 27 | 28 | """ 29 | pass 30 | 31 | def lookupNearby(self, location, category, query, countryAreaCode): 32 | """ 33 | Parameters: 34 | - location 35 | - category 36 | - query 37 | - countryAreaCode 38 | 39 | """ 40 | pass 41 | 42 | 43 | class Client(Iface): 44 | def __init__(self, iprot, oprot=None): 45 | self._iprot = self._oprot = iprot 46 | if oprot is not None: 47 | self._oprot = oprot 48 | self._seqid = 0 49 | 50 | def lookupByPhoneNumber(self, countryAreaCode, phoneNumber): 51 | """ 52 | Parameters: 53 | - countryAreaCode 54 | - phoneNumber 55 | 56 | """ 57 | self.send_lookupByPhoneNumber(countryAreaCode, phoneNumber) 58 | return self.recv_lookupByPhoneNumber() 59 | 60 | def send_lookupByPhoneNumber(self, countryAreaCode, phoneNumber): 61 | self._oprot.writeMessageBegin('lookupByPhoneNumber', TMessageType.CALL, self._seqid) 62 | args = lookupByPhoneNumber_args() 63 | args.countryAreaCode = countryAreaCode 64 | args.phoneNumber = phoneNumber 65 | args.write(self._oprot) 66 | self._oprot.writeMessageEnd() 67 | self._oprot.trans.flush() 68 | 69 | def recv_lookupByPhoneNumber(self): 70 | iprot = self._iprot 71 | (fname, mtype, rseqid) = iprot.readMessageBegin() 72 | if mtype == TMessageType.EXCEPTION: 73 | x = TApplicationException() 74 | x.read(iprot) 75 | iprot.readMessageEnd() 76 | raise x 77 | result = lookupByPhoneNumber_result() 78 | result.read(iprot) 79 | iprot.readMessageEnd() 80 | if result.success is not None: 81 | return result.success 82 | if result.e is not None: 83 | raise result.e 84 | raise TApplicationException(TApplicationException.MISSING_RESULT, "lookupByPhoneNumber failed: unknown result") 85 | 86 | def lookupNearby(self, location, category, query, countryAreaCode): 87 | """ 88 | Parameters: 89 | - location 90 | - category 91 | - query 92 | - countryAreaCode 93 | 94 | """ 95 | self.send_lookupNearby(location, category, query, countryAreaCode) 96 | return self.recv_lookupNearby() 97 | 98 | def send_lookupNearby(self, location, category, query, countryAreaCode): 99 | self._oprot.writeMessageBegin('lookupNearby', TMessageType.CALL, self._seqid) 100 | args = lookupNearby_args() 101 | args.location = location 102 | args.category = category 103 | args.query = query 104 | args.countryAreaCode = countryAreaCode 105 | args.write(self._oprot) 106 | self._oprot.writeMessageEnd() 107 | self._oprot.trans.flush() 108 | 109 | def recv_lookupNearby(self): 110 | iprot = self._iprot 111 | (fname, mtype, rseqid) = iprot.readMessageBegin() 112 | if mtype == TMessageType.EXCEPTION: 113 | x = TApplicationException() 114 | x.read(iprot) 115 | iprot.readMessageEnd() 116 | raise x 117 | result = lookupNearby_result() 118 | result.read(iprot) 119 | iprot.readMessageEnd() 120 | if result.success is not None: 121 | return result.success 122 | if result.e is not None: 123 | raise result.e 124 | raise TApplicationException(TApplicationException.MISSING_RESULT, "lookupNearby failed: unknown result") 125 | 126 | 127 | class Processor(Iface, TProcessor): 128 | def __init__(self, handler): 129 | self._handler = handler 130 | self._processMap = {} 131 | self._processMap["lookupByPhoneNumber"] = Processor.process_lookupByPhoneNumber 132 | self._processMap["lookupNearby"] = Processor.process_lookupNearby 133 | 134 | def process(self, iprot, oprot): 135 | (name, type, seqid) = iprot.readMessageBegin() 136 | if name not in self._processMap: 137 | iprot.skip(TType.STRUCT) 138 | iprot.readMessageEnd() 139 | x = TApplicationException(TApplicationException.UNKNOWN_METHOD, 'Unknown function %s' % (name)) 140 | oprot.writeMessageBegin(name, TMessageType.EXCEPTION, seqid) 141 | x.write(oprot) 142 | oprot.writeMessageEnd() 143 | oprot.trans.flush() 144 | return 145 | else: 146 | self._processMap[name](self, seqid, iprot, oprot) 147 | return True 148 | 149 | def process_lookupByPhoneNumber(self, seqid, iprot, oprot): 150 | args = lookupByPhoneNumber_args() 151 | args.read(iprot) 152 | iprot.readMessageEnd() 153 | result = lookupByPhoneNumber_result() 154 | try: 155 | result.success = self._handler.lookupByPhoneNumber(args.countryAreaCode, args.phoneNumber) 156 | msg_type = TMessageType.REPLY 157 | except TTransport.TTransportException: 158 | raise 159 | except TalkException as e: 160 | msg_type = TMessageType.REPLY 161 | result.e = e 162 | except TApplicationException as ex: 163 | logging.exception('TApplication exception in handler') 164 | msg_type = TMessageType.EXCEPTION 165 | result = ex 166 | except Exception: 167 | logging.exception('Unexpected exception in handler') 168 | msg_type = TMessageType.EXCEPTION 169 | result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') 170 | oprot.writeMessageBegin("lookupByPhoneNumber", msg_type, seqid) 171 | result.write(oprot) 172 | oprot.writeMessageEnd() 173 | oprot.trans.flush() 174 | 175 | def process_lookupNearby(self, seqid, iprot, oprot): 176 | args = lookupNearby_args() 177 | args.read(iprot) 178 | iprot.readMessageEnd() 179 | result = lookupNearby_result() 180 | try: 181 | result.success = self._handler.lookupNearby(args.location, args.category, args.query, args.countryAreaCode) 182 | msg_type = TMessageType.REPLY 183 | except TTransport.TTransportException: 184 | raise 185 | except TalkException as e: 186 | msg_type = TMessageType.REPLY 187 | result.e = e 188 | except TApplicationException as ex: 189 | logging.exception('TApplication exception in handler') 190 | msg_type = TMessageType.EXCEPTION 191 | result = ex 192 | except Exception: 193 | logging.exception('Unexpected exception in handler') 194 | msg_type = TMessageType.EXCEPTION 195 | result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') 196 | oprot.writeMessageBegin("lookupNearby", msg_type, seqid) 197 | result.write(oprot) 198 | oprot.writeMessageEnd() 199 | oprot.trans.flush() 200 | 201 | # HELPER FUNCTIONS AND STRUCTURES 202 | 203 | 204 | class lookupByPhoneNumber_args(object): 205 | """ 206 | Attributes: 207 | - countryAreaCode 208 | - phoneNumber 209 | 210 | """ 211 | 212 | 213 | def __init__(self, countryAreaCode=None, phoneNumber=None,): 214 | self.countryAreaCode = countryAreaCode 215 | self.phoneNumber = phoneNumber 216 | 217 | def read(self, iprot): 218 | if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: 219 | iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) 220 | return 221 | iprot.readStructBegin() 222 | while True: 223 | (fname, ftype, fid) = iprot.readFieldBegin() 224 | if ftype == TType.STOP: 225 | break 226 | if fid == 2: 227 | if ftype == TType.STRING: 228 | self.countryAreaCode = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() 229 | else: 230 | iprot.skip(ftype) 231 | elif fid == 3: 232 | if ftype == TType.STRING: 233 | self.phoneNumber = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() 234 | else: 235 | iprot.skip(ftype) 236 | else: 237 | iprot.skip(ftype) 238 | iprot.readFieldEnd() 239 | iprot.readStructEnd() 240 | 241 | def write(self, oprot): 242 | if oprot._fast_encode is not None and self.thrift_spec is not None: 243 | oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) 244 | return 245 | oprot.writeStructBegin('lookupByPhoneNumber_args') 246 | if self.countryAreaCode is not None: 247 | oprot.writeFieldBegin('countryAreaCode', TType.STRING, 2) 248 | oprot.writeString(self.countryAreaCode.encode('utf-8') if sys.version_info[0] == 2 else self.countryAreaCode) 249 | oprot.writeFieldEnd() 250 | if self.phoneNumber is not None: 251 | oprot.writeFieldBegin('phoneNumber', TType.STRING, 3) 252 | oprot.writeString(self.phoneNumber.encode('utf-8') if sys.version_info[0] == 2 else self.phoneNumber) 253 | oprot.writeFieldEnd() 254 | oprot.writeFieldStop() 255 | oprot.writeStructEnd() 256 | 257 | def validate(self): 258 | return 259 | 260 | def __repr__(self): 261 | L = ['%s=%r' % (key, value) 262 | for key, value in self.__dict__.items()] 263 | return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) 264 | 265 | def __eq__(self, other): 266 | return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ 267 | 268 | def __ne__(self, other): 269 | return not (self == other) 270 | all_structs.append(lookupByPhoneNumber_args) 271 | lookupByPhoneNumber_args.thrift_spec = ( 272 | None, # 0 273 | None, # 1 274 | (2, TType.STRING, 'countryAreaCode', 'UTF8', None, ), # 2 275 | (3, TType.STRING, 'phoneNumber', 'UTF8', None, ), # 3 276 | ) 277 | 278 | 279 | class lookupByPhoneNumber_result(object): 280 | """ 281 | Attributes: 282 | - success 283 | - e 284 | 285 | """ 286 | 287 | 288 | def __init__(self, success=None, e=None,): 289 | self.success = success 290 | self.e = e 291 | 292 | def read(self, iprot): 293 | if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: 294 | iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) 295 | return 296 | iprot.readStructBegin() 297 | while True: 298 | (fname, ftype, fid) = iprot.readFieldBegin() 299 | if ftype == TType.STOP: 300 | break 301 | if fid == 0: 302 | if ftype == TType.STRUCT: 303 | self.success = SpotPhoneNumberResponse() 304 | self.success.read(iprot) 305 | else: 306 | iprot.skip(ftype) 307 | elif fid == 1: 308 | if ftype == TType.STRUCT: 309 | self.e = TalkException() 310 | self.e.read(iprot) 311 | else: 312 | iprot.skip(ftype) 313 | else: 314 | iprot.skip(ftype) 315 | iprot.readFieldEnd() 316 | iprot.readStructEnd() 317 | 318 | def write(self, oprot): 319 | if oprot._fast_encode is not None and self.thrift_spec is not None: 320 | oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) 321 | return 322 | oprot.writeStructBegin('lookupByPhoneNumber_result') 323 | if self.success is not None: 324 | oprot.writeFieldBegin('success', TType.STRUCT, 0) 325 | self.success.write(oprot) 326 | oprot.writeFieldEnd() 327 | if self.e is not None: 328 | oprot.writeFieldBegin('e', TType.STRUCT, 1) 329 | self.e.write(oprot) 330 | oprot.writeFieldEnd() 331 | oprot.writeFieldStop() 332 | oprot.writeStructEnd() 333 | 334 | def validate(self): 335 | return 336 | 337 | def __repr__(self): 338 | L = ['%s=%r' % (key, value) 339 | for key, value in self.__dict__.items()] 340 | return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) 341 | 342 | def __eq__(self, other): 343 | return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ 344 | 345 | def __ne__(self, other): 346 | return not (self == other) 347 | all_structs.append(lookupByPhoneNumber_result) 348 | lookupByPhoneNumber_result.thrift_spec = ( 349 | (0, TType.STRUCT, 'success', [SpotPhoneNumberResponse, None], None, ), # 0 350 | (1, TType.STRUCT, 'e', [TalkException, None], None, ), # 1 351 | ) 352 | 353 | 354 | class lookupNearby_args(object): 355 | """ 356 | Attributes: 357 | - location 358 | - category 359 | - query 360 | - countryAreaCode 361 | 362 | """ 363 | 364 | 365 | def __init__(self, location=None, category=None, query=None, countryAreaCode=None,): 366 | self.location = location 367 | self.category = category 368 | self.query = query 369 | self.countryAreaCode = countryAreaCode 370 | 371 | def read(self, iprot): 372 | if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: 373 | iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) 374 | return 375 | iprot.readStructBegin() 376 | while True: 377 | (fname, ftype, fid) = iprot.readFieldBegin() 378 | if ftype == TType.STOP: 379 | break 380 | if fid == 2: 381 | if ftype == TType.STRUCT: 382 | self.location = Location() 383 | self.location.read(iprot) 384 | else: 385 | iprot.skip(ftype) 386 | elif fid == 3: 387 | if ftype == TType.I32: 388 | self.category = iprot.readI32() 389 | else: 390 | iprot.skip(ftype) 391 | elif fid == 4: 392 | if ftype == TType.STRING: 393 | self.query = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() 394 | else: 395 | iprot.skip(ftype) 396 | elif fid == 5: 397 | if ftype == TType.STRING: 398 | self.countryAreaCode = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() 399 | else: 400 | iprot.skip(ftype) 401 | else: 402 | iprot.skip(ftype) 403 | iprot.readFieldEnd() 404 | iprot.readStructEnd() 405 | 406 | def write(self, oprot): 407 | if oprot._fast_encode is not None and self.thrift_spec is not None: 408 | oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) 409 | return 410 | oprot.writeStructBegin('lookupNearby_args') 411 | if self.location is not None: 412 | oprot.writeFieldBegin('location', TType.STRUCT, 2) 413 | self.location.write(oprot) 414 | oprot.writeFieldEnd() 415 | if self.category is not None: 416 | oprot.writeFieldBegin('category', TType.I32, 3) 417 | oprot.writeI32(self.category) 418 | oprot.writeFieldEnd() 419 | if self.query is not None: 420 | oprot.writeFieldBegin('query', TType.STRING, 4) 421 | oprot.writeString(self.query.encode('utf-8') if sys.version_info[0] == 2 else self.query) 422 | oprot.writeFieldEnd() 423 | if self.countryAreaCode is not None: 424 | oprot.writeFieldBegin('countryAreaCode', TType.STRING, 5) 425 | oprot.writeString(self.countryAreaCode.encode('utf-8') if sys.version_info[0] == 2 else self.countryAreaCode) 426 | oprot.writeFieldEnd() 427 | oprot.writeFieldStop() 428 | oprot.writeStructEnd() 429 | 430 | def validate(self): 431 | return 432 | 433 | def __repr__(self): 434 | L = ['%s=%r' % (key, value) 435 | for key, value in self.__dict__.items()] 436 | return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) 437 | 438 | def __eq__(self, other): 439 | return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ 440 | 441 | def __ne__(self, other): 442 | return not (self == other) 443 | all_structs.append(lookupNearby_args) 444 | lookupNearby_args.thrift_spec = ( 445 | None, # 0 446 | None, # 1 447 | (2, TType.STRUCT, 'location', [Location, None], None, ), # 2 448 | (3, TType.I32, 'category', None, None, ), # 3 449 | (4, TType.STRING, 'query', 'UTF8', None, ), # 4 450 | (5, TType.STRING, 'countryAreaCode', 'UTF8', None, ), # 5 451 | ) 452 | 453 | 454 | class lookupNearby_result(object): 455 | """ 456 | Attributes: 457 | - success 458 | - e 459 | 460 | """ 461 | 462 | 463 | def __init__(self, success=None, e=None,): 464 | self.success = success 465 | self.e = e 466 | 467 | def read(self, iprot): 468 | if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: 469 | iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) 470 | return 471 | iprot.readStructBegin() 472 | while True: 473 | (fname, ftype, fid) = iprot.readFieldBegin() 474 | if ftype == TType.STOP: 475 | break 476 | if fid == 0: 477 | if ftype == TType.STRUCT: 478 | self.success = SpotNearbyResponse() 479 | self.success.read(iprot) 480 | else: 481 | iprot.skip(ftype) 482 | elif fid == 1: 483 | if ftype == TType.STRUCT: 484 | self.e = TalkException() 485 | self.e.read(iprot) 486 | else: 487 | iprot.skip(ftype) 488 | else: 489 | iprot.skip(ftype) 490 | iprot.readFieldEnd() 491 | iprot.readStructEnd() 492 | 493 | def write(self, oprot): 494 | if oprot._fast_encode is not None and self.thrift_spec is not None: 495 | oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) 496 | return 497 | oprot.writeStructBegin('lookupNearby_result') 498 | if self.success is not None: 499 | oprot.writeFieldBegin('success', TType.STRUCT, 0) 500 | self.success.write(oprot) 501 | oprot.writeFieldEnd() 502 | if self.e is not None: 503 | oprot.writeFieldBegin('e', TType.STRUCT, 1) 504 | self.e.write(oprot) 505 | oprot.writeFieldEnd() 506 | oprot.writeFieldStop() 507 | oprot.writeStructEnd() 508 | 509 | def validate(self): 510 | return 511 | 512 | def __repr__(self): 513 | L = ['%s=%r' % (key, value) 514 | for key, value in self.__dict__.items()] 515 | return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) 516 | 517 | def __eq__(self, other): 518 | return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ 519 | 520 | def __ne__(self, other): 521 | return not (self == other) 522 | all_structs.append(lookupNearby_result) 523 | lookupNearby_result.thrift_spec = ( 524 | (0, TType.STRUCT, 'success', [SpotNearbyResponse, None], None, ), # 0 525 | (1, TType.STRUCT, 'e', [TalkException, None], None, ), # 1 526 | ) 527 | fix_spec(all_structs) 528 | del all_structs 529 | 530 | -------------------------------------------------------------------------------- /akad/UniversalNotificationService.py: -------------------------------------------------------------------------------- 1 | # 2 | # Autogenerated by Thrift Compiler (0.12.0) 3 | # 4 | # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 5 | # 6 | # options string: py 7 | # 8 | 9 | from thrift.Thrift import TType, TMessageType, TFrozenDict, TException, TApplicationException 10 | from thrift.protocol.TProtocol import TProtocolException 11 | from thrift.TRecursive import fix_spec 12 | 13 | import sys 14 | import logging 15 | from .ttypes import * 16 | from thrift.Thrift import TProcessor 17 | from thrift.transport import TTransport 18 | all_structs = [] 19 | 20 | 21 | class Iface(object): 22 | def notify(self, event): 23 | """ 24 | Parameters: 25 | - event 26 | 27 | """ 28 | pass 29 | 30 | 31 | class Client(Iface): 32 | def __init__(self, iprot, oprot=None): 33 | self._iprot = self._oprot = iprot 34 | if oprot is not None: 35 | self._oprot = oprot 36 | self._seqid = 0 37 | 38 | def notify(self, event): 39 | """ 40 | Parameters: 41 | - event 42 | 43 | """ 44 | self.send_notify(event) 45 | self.recv_notify() 46 | 47 | def send_notify(self, event): 48 | self._oprot.writeMessageBegin('notify', TMessageType.CALL, self._seqid) 49 | args = notify_args() 50 | args.event = event 51 | args.write(self._oprot) 52 | self._oprot.writeMessageEnd() 53 | self._oprot.trans.flush() 54 | 55 | def recv_notify(self): 56 | iprot = self._iprot 57 | (fname, mtype, rseqid) = iprot.readMessageBegin() 58 | if mtype == TMessageType.EXCEPTION: 59 | x = TApplicationException() 60 | x.read(iprot) 61 | iprot.readMessageEnd() 62 | raise x 63 | result = notify_result() 64 | result.read(iprot) 65 | iprot.readMessageEnd() 66 | if result.e is not None: 67 | raise result.e 68 | return 69 | 70 | 71 | class Processor(Iface, TProcessor): 72 | def __init__(self, handler): 73 | self._handler = handler 74 | self._processMap = {} 75 | self._processMap["notify"] = Processor.process_notify 76 | 77 | def process(self, iprot, oprot): 78 | (name, type, seqid) = iprot.readMessageBegin() 79 | if name not in self._processMap: 80 | iprot.skip(TType.STRUCT) 81 | iprot.readMessageEnd() 82 | x = TApplicationException(TApplicationException.UNKNOWN_METHOD, 'Unknown function %s' % (name)) 83 | oprot.writeMessageBegin(name, TMessageType.EXCEPTION, seqid) 84 | x.write(oprot) 85 | oprot.writeMessageEnd() 86 | oprot.trans.flush() 87 | return 88 | else: 89 | self._processMap[name](self, seqid, iprot, oprot) 90 | return True 91 | 92 | def process_notify(self, seqid, iprot, oprot): 93 | args = notify_args() 94 | args.read(iprot) 95 | iprot.readMessageEnd() 96 | result = notify_result() 97 | try: 98 | self._handler.notify(args.event) 99 | msg_type = TMessageType.REPLY 100 | except TTransport.TTransportException: 101 | raise 102 | except UniversalNotificationServiceException as e: 103 | msg_type = TMessageType.REPLY 104 | result.e = e 105 | except TApplicationException as ex: 106 | logging.exception('TApplication exception in handler') 107 | msg_type = TMessageType.EXCEPTION 108 | result = ex 109 | except Exception: 110 | logging.exception('Unexpected exception in handler') 111 | msg_type = TMessageType.EXCEPTION 112 | result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') 113 | oprot.writeMessageBegin("notify", msg_type, seqid) 114 | result.write(oprot) 115 | oprot.writeMessageEnd() 116 | oprot.trans.flush() 117 | 118 | # HELPER FUNCTIONS AND STRUCTURES 119 | 120 | 121 | class notify_args(object): 122 | """ 123 | Attributes: 124 | - event 125 | 126 | """ 127 | 128 | 129 | def __init__(self, event=None,): 130 | self.event = event 131 | 132 | def read(self, iprot): 133 | if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: 134 | iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) 135 | return 136 | iprot.readStructBegin() 137 | while True: 138 | (fname, ftype, fid) = iprot.readFieldBegin() 139 | if ftype == TType.STOP: 140 | break 141 | if fid == 2: 142 | if ftype == TType.STRUCT: 143 | self.event = GlobalEvent() 144 | self.event.read(iprot) 145 | else: 146 | iprot.skip(ftype) 147 | else: 148 | iprot.skip(ftype) 149 | iprot.readFieldEnd() 150 | iprot.readStructEnd() 151 | 152 | def write(self, oprot): 153 | if oprot._fast_encode is not None and self.thrift_spec is not None: 154 | oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) 155 | return 156 | oprot.writeStructBegin('notify_args') 157 | if self.event is not None: 158 | oprot.writeFieldBegin('event', TType.STRUCT, 2) 159 | self.event.write(oprot) 160 | oprot.writeFieldEnd() 161 | oprot.writeFieldStop() 162 | oprot.writeStructEnd() 163 | 164 | def validate(self): 165 | return 166 | 167 | def __repr__(self): 168 | L = ['%s=%r' % (key, value) 169 | for key, value in self.__dict__.items()] 170 | return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) 171 | 172 | def __eq__(self, other): 173 | return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ 174 | 175 | def __ne__(self, other): 176 | return not (self == other) 177 | all_structs.append(notify_args) 178 | notify_args.thrift_spec = ( 179 | None, # 0 180 | None, # 1 181 | (2, TType.STRUCT, 'event', [GlobalEvent, None], None, ), # 2 182 | ) 183 | 184 | 185 | class notify_result(object): 186 | """ 187 | Attributes: 188 | - e 189 | 190 | """ 191 | 192 | 193 | def __init__(self, e=None,): 194 | self.e = e 195 | 196 | def read(self, iprot): 197 | if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: 198 | iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) 199 | return 200 | iprot.readStructBegin() 201 | while True: 202 | (fname, ftype, fid) = iprot.readFieldBegin() 203 | if ftype == TType.STOP: 204 | break 205 | if fid == 1: 206 | if ftype == TType.STRUCT: 207 | self.e = UniversalNotificationServiceException() 208 | self.e.read(iprot) 209 | else: 210 | iprot.skip(ftype) 211 | else: 212 | iprot.skip(ftype) 213 | iprot.readFieldEnd() 214 | iprot.readStructEnd() 215 | 216 | def write(self, oprot): 217 | if oprot._fast_encode is not None and self.thrift_spec is not None: 218 | oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) 219 | return 220 | oprot.writeStructBegin('notify_result') 221 | if self.e is not None: 222 | oprot.writeFieldBegin('e', TType.STRUCT, 1) 223 | self.e.write(oprot) 224 | oprot.writeFieldEnd() 225 | oprot.writeFieldStop() 226 | oprot.writeStructEnd() 227 | 228 | def validate(self): 229 | return 230 | 231 | def __repr__(self): 232 | L = ['%s=%r' % (key, value) 233 | for key, value in self.__dict__.items()] 234 | return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) 235 | 236 | def __eq__(self, other): 237 | return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ 238 | 239 | def __ne__(self, other): 240 | return not (self == other) 241 | all_structs.append(notify_result) 242 | notify_result.thrift_spec = ( 243 | None, # 0 244 | (1, TType.STRUCT, 'e', [UniversalNotificationServiceException, None], None, ), # 1 245 | ) 246 | fix_spec(all_structs) 247 | del all_structs 248 | 249 | -------------------------------------------------------------------------------- /akad/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ['ttypes', 'constants', 'AccountSupervisorService', 'SpotService', 'BotService', 'LiffService', 'AgeCheckService', 'BuddyManagementService', 'BuddyService', 'ChannelApplicationProvidedService', 'ChannelService', 'MessageService', 'SnsAdaptorService', 'TalkService', 'UniversalNotificationService', 'CallService', 'AuthService', 'SquareService', 'TicketService', 'LongpollingService', 'ShopService'] 2 | -------------------------------------------------------------------------------- /akad/constants.py: -------------------------------------------------------------------------------- 1 | # 2 | # Autogenerated by Thrift Compiler (0.12.0) 3 | # 4 | # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 5 | # 6 | # options string: py 7 | # 8 | 9 | from thrift.Thrift import TType, TMessageType, TFrozenDict, TException, TApplicationException 10 | from thrift.protocol.TProtocol import TProtocolException 11 | from thrift.TRecursive import fix_spec 12 | 13 | import sys 14 | from .ttypes import * 15 | -------------------------------------------------------------------------------- /linepy/__init__.py: -------------------------------------------------------------------------------- 1 | from .client import LINE 2 | from .channel import Channel 3 | from .oepoll import OEPoll 4 | from akad.ttypes import OpType 5 | 6 | __modified__ = 'Zero Cool' 7 | __copyright__ = 'Copyright 2018 by Fadhiil Rachman' 8 | __version__ = '3.0.8' 9 | __license__ = 'BSD-3-Clause' 10 | __author__ = 'Fadhiil Rachman' 11 | __author_email__ = 'fadhiilrachman@gmail.com' 12 | __url__ = 'http://github.com/fadhiilrachman/line-py' 13 | 14 | __all__ = ['LINE', 'Channel', 'OEPoll', 'OpType', '__modified__'] 15 | -------------------------------------------------------------------------------- /linepy/auth.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from akad.ttypes import IdentityProvider, LoginResultType, LoginRequest, LoginType, E2EEKeyChain 3 | from .server import Server 4 | from .session import Session 5 | from .callback import Callback 6 | from .login import QRLogin 7 | import rsa, os 8 | 9 | class Auth(object): 10 | isLogin = False 11 | authToken = "" 12 | certificate = "" 13 | 14 | def __init__(self): 15 | self.server = Server(self.appType) 16 | self.callback = Callback(self.__defaultCallback) 17 | self.server.setHeadersWithDict({ 18 | 'User-Agent': self.server.USER_AGENT, 19 | 'X-Line-Application': self.server.APP_NAME, 20 | 'X-Line-Carrier': self.server.CARRIER, 21 | 'x-lal': 'en_US' 22 | }) 23 | 24 | def __loadSession(self): 25 | self.talk = Session(self.server.LINE_HOST_DOMAIN, self.server.Headers, self.server.LINE_API_QUERY_PATH_FIR, self.customThrift).Talk() 26 | self.poll = Session(self.server.LINE_HOST_DOMAIN, self.server.Headers, self.server.LINE_POLL_QUERY_PATH_FIR, self.customThrift).Talk() 27 | self.call = Session(self.server.LINE_HOST_DOMAIN, self.server.Headers, self.server.LINE_CALL_QUERY_PATH, self.customThrift).Call() 28 | self.channel = Session(self.server.LINE_HOST_DOMAIN, self.server.Headers, self.server.LINE_CHAN_QUERY_PATH, self.customThrift).Channel() 29 | self.square = Session(self.server.LINE_HOST_DOMAIN, self.server.Headers, self.server.LINE_SQUARE_QUERY_PATH, self.customThrift).Square() 30 | self.liff = Session(self.server.LINE_HOST_DOMAIN, self.server.Headers, self.server.LINE_LIFF_QUERY_PATH, self.customThrift).Liff() 31 | self.shop = Session(self.server.LINE_HOST_DOMAIN, self.server.Headers, self.server.LINE_SHOP_QUERY_PATH, self.customThrift).Shop() 32 | 33 | self.revision = self.poll.getLastOpRevision() 34 | self.isLogin = True 35 | 36 | def __loginRequest(self, type, data): 37 | lReq = LoginRequest() 38 | if type == '0': 39 | lReq.type = LoginType.ID_CREDENTIAL 40 | lReq.identityProvider = data['identityProvider'] 41 | lReq.identifier = data['identifier'] 42 | lReq.password = data['password'] 43 | lReq.keepLoggedIn = data['keepLoggedIn'] 44 | lReq.accessLocation = data['accessLocation'] 45 | lReq.systemName = data['systemName'] 46 | lReq.certificate = data['certificate'] 47 | lReq.e2eeVersion = data['e2eeVersion'] 48 | elif type == '1': 49 | lReq.type = LoginType.QRCODE 50 | lReq.keepLoggedIn = data['keepLoggedIn'] 51 | if 'identityProvider' in data: 52 | lReq.identityProvider = data['identityProvider'] 53 | if 'accessLocation' in data: 54 | lReq.accessLocation = data['accessLocation'] 55 | if 'systemName' in data: 56 | lReq.systemName = data['systemName'] 57 | lReq.verifier = data['verifier'] 58 | lReq.e2eeVersion = data['e2eeVersion'] 59 | else: 60 | lReq=False 61 | return lReq 62 | 63 | def loginWithCredential(self, _id, passwd): 64 | if self.systemName is None: 65 | self.systemName=self.server.SYSTEM_NAME 66 | if self.server.EMAIL_REGEX.match(_id): 67 | self.provider = IdentityProvider.LINE # LINE 68 | else: 69 | self.provider = IdentityProvider.NAVER_KR # NAVER 70 | 71 | if self.appName is None: 72 | self.appName=self.server.APP_NAME 73 | self.server.setHeaders('X-Line-Application', self.appName) 74 | self.tauth = Session(self.server.LINE_HOST_DOMAIN, self.server.Headers, self.server.LINE_AUTH_QUERY_PATH).Talk(isopen=False) 75 | 76 | rsaKey = self.tauth.getRSAKeyInfo(self.provider) 77 | 78 | message = (chr(len(rsaKey.sessionKey)) + rsaKey.sessionKey + 79 | chr(len(_id)) + _id + 80 | chr(len(passwd)) + passwd).encode('utf-8') 81 | pub_key = rsa.PublicKey(int(rsaKey.nvalue, 16), int(rsaKey.evalue, 16)) 82 | crypto = rsa.encrypt(message, pub_key).hex() 83 | 84 | try: 85 | with open(_id + '.crt', 'r') as f: 86 | self.certificate = f.read() 87 | except: 88 | if self.certificate is not None: 89 | if os.path.exists(self.certificate): 90 | with open(self.certificate, 'r') as f: 91 | self.certificate = f.read() 92 | 93 | self.auth = Session(self.server.LINE_HOST_DOMAIN, self.server.Headers, self.server.LINE_LOGIN_QUERY_PATH).Auth(isopen=False) 94 | 95 | lReq = self.__loginRequest('0', { 96 | 'identityProvider': self.provider, 97 | 'identifier': rsaKey.keynm, 98 | 'password': crypto, 99 | 'keepLoggedIn': self.keepLoggedIn, 100 | 'accessLocation': self.server.IP_ADDR, 101 | 'systemName': self.systemName, 102 | 'certificate': self.certificate, 103 | 'e2eeVersion': 0 104 | }) 105 | 106 | result = self.auth.loginZ(lReq) 107 | 108 | if result.type == LoginResultType.REQUIRE_DEVICE_CONFIRM: 109 | self.callback.PinVerified(result.pinCode) 110 | 111 | self.server.setHeaders('X-Line-Access', result.verifier) 112 | getAccessKey = self.server.getJson(self.server.parseUrl(self.server.LINE_CERTIFICATE_PATH), allowHeader=True) 113 | 114 | self.auth = Session(self.server.LINE_HOST_DOMAIN, self.server.Headers, self.server.LINE_LOGIN_QUERY_PATH).Auth(isopen=False) 115 | 116 | try: 117 | lReq = self.__loginRequest('1', { 118 | 'keepLoggedIn': self.keepLoggedIn, 119 | 'verifier': getAccessKey['result']['verifier'], 120 | 'e2eeVersion': 0 121 | }) 122 | result = self.auth.loginZ(lReq) 123 | except: 124 | raise Exception('Login failed') 125 | 126 | if result.type == LoginResultType.SUCCESS: 127 | if result.certificate is not None: 128 | with open(_id + '.crt', 'w') as f: 129 | f.write(result.certificate) 130 | self.certificate = result.certificate 131 | if result.authToken is not None: 132 | self.loginWithAuthToken(result.authToken) 133 | else: 134 | return False 135 | else: 136 | raise Exception('Login failed') 137 | 138 | elif result.type == LoginResultType.REQUIRE_QRCODE: 139 | self.loginWithQrCode() 140 | pass 141 | 142 | elif result.type == LoginResultType.SUCCESS: 143 | self.certificate = result.certificate 144 | self.loginWithAuthToken(result.authToken) 145 | 146 | def loginWithQrCode(self): 147 | newqr = QRLogin() 148 | application = self.server.APP_NAME.split("\t")[0].lower() 149 | if application == "androidlite": 150 | application = "android_lite" 151 | elif application == "iosipad": 152 | application = "ios_ipad" 153 | result = newqr.loginWithQrCode(application, callback=self.callback.callback) 154 | self.loginWithAuthToken(result.accessToken) 155 | 156 | def loginWithAuthToken(self, authToken=None): 157 | if authToken is None: 158 | raise Exception('Please provide Auth Token') 159 | if self.appName is None: 160 | self.appName=self.server.APP_NAME 161 | self.server.setHeadersWithDict({ 162 | 'X-Line-Application': self.appName, 163 | 'X-Line-Access': authToken 164 | }) 165 | self.authToken = authToken 166 | self.__loadSession() 167 | 168 | def __defaultCallback(self, str): 169 | print(str) 170 | 171 | def logout(self): 172 | self.isLogin = False 173 | self.auth.logoutZ() 174 | -------------------------------------------------------------------------------- /linepy/call.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from akad.ttypes import MediaType 3 | 4 | def loggedIn(func): 5 | def checkLogin(*args, **kwargs): 6 | if args[0].isLogin: 7 | return func(*args, **kwargs) 8 | else: 9 | args[0].callback.default('You want to call the function, you must login to LINE') 10 | return checkLogin 11 | 12 | class Call(object): 13 | isLogin = False 14 | 15 | def __init__(self): 16 | self.isLogin = True 17 | 18 | @loggedIn 19 | def acquireCallRoute(self, to): 20 | return self.call.acquireCallRoute(to) 21 | 22 | @loggedIn 23 | def acquireGroupCallRoute(self, groupId, mediaType=MediaType.AUDIO): 24 | return self.call.acquireGroupCallRoute(groupId, mediaType) 25 | 26 | @loggedIn 27 | def getGroupCall(self, ChatMid): 28 | return self.call.getGroupCall(ChatMid) 29 | 30 | @loggedIn 31 | def inviteIntoGroupCall(self, chatId, contactIds=[], mediaType=MediaType.AUDIO): 32 | return self.call.inviteIntoGroupCall(chatId, contactIds, mediaType) 33 | -------------------------------------------------------------------------------- /linepy/callback.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | class Callback(object): 3 | 4 | def __init__(self, callback): 5 | self.callback = callback 6 | 7 | def PinVerified(self, pin): 8 | self.callback("Input this PIN code '" + pin + "' on your LINE for smartphone in 2 minutes") 9 | 10 | def QrUrl(self, url, showQr=True): 11 | if showQr: 12 | notice='or scan this QR ' 13 | else: 14 | notice='' 15 | self.callback('Open this link ' + notice + 'on your LINE for smartphone in 2 minutes\n' + url) 16 | if showQr: 17 | try: 18 | import pyqrcode 19 | url = pyqrcode.create(url) 20 | self.callback(url.terminal('green', 'white', 1)) 21 | except: 22 | pass 23 | 24 | def default(self, str): 25 | self.callback(str) -------------------------------------------------------------------------------- /linepy/channel.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | def loggedIn(func): 4 | def checkLogin(*args, **kwargs): 5 | if args[0].isLogin: 6 | return func(*args, **kwargs) 7 | else: 8 | args[0].callback.default('You want to call the function, you must login to LINE') 9 | return checkLogin 10 | 11 | class Channel(object): 12 | isLogin = False 13 | channelId = None 14 | channelResult = None 15 | 16 | def __init__(self, client, channelId, showSuccess=True): 17 | self.client = client 18 | self.channelId = channelId 19 | self.showSuccess = showSuccess 20 | self.__loginChannel() 21 | 22 | def __logChannel(self, text): 23 | self.client.log('[%s] : Success login to %s' % (self.client.profile.displayName, text)) 24 | 25 | def __loginChannel(self): 26 | self.isLogin = True 27 | self.channelResult = self.approveChannelAndIssueChannelToken(self.channelId) 28 | self.__createChannelSession() 29 | 30 | @loggedIn 31 | def getChannelResult(self): 32 | return self.channelResult 33 | 34 | def __createChannelSession(self): 35 | channelInfo = self.getChannelInfo(self.channelId) 36 | if self.showSuccess: 37 | self.__logChannel(channelInfo.name) 38 | 39 | @loggedIn 40 | def approveChannelAndIssueChannelToken(self, channelId): 41 | return self.client.approveChannelAndIssueChannelToken(channelId) 42 | 43 | @loggedIn 44 | def issueChannelToken(self, channelId): 45 | return self.client.issueChannelToken(channelId) 46 | 47 | @loggedIn 48 | def getChannelInfo(self, channelId, locale='ID'): 49 | return self.client.getChannelInfo(channelId, locale) 50 | 51 | @loggedIn 52 | def revokeChannel(self, channelId): 53 | return self.client.revokeChannel(channelId) 54 | -------------------------------------------------------------------------------- /linepy/client.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from akad.ttypes import Message 3 | from .auth import Auth 4 | from .models import Models 5 | from .talk import Talk 6 | from .square import Square 7 | from .call import Call 8 | from .timeline import Timeline 9 | from .server import Server 10 | from .liff import Liff 11 | from .shop import Shop 12 | from .callback import Callback 13 | from .e2ee import E2EE 14 | 15 | class LINE(Auth, Models, Talk, Square, Call, Timeline, Liff, Shop, E2EE): 16 | 17 | def __init__(self, idOrAuthToken=None, passwd=None, **kwargs): 18 | """ 19 | :param idOrAuthToken: Login email, token. Default: None 20 | :param passwd: Login password. Default: None 21 | :param kwargs: See below 22 | :Keyword Arguments: 23 | - **certificate**: Line certificate after email login. Default: None 24 | - **systemName**: System name when first login. Default: None 25 | - **appType**: Application type to login. Default: None 26 | - **appName**: Application name to login. Default: None 27 | - **showQr**: Print out qr code. Default: False 28 | - **channelId**: Channel ID to login Timeline. Default: None 29 | - **keepLoggedIn**: Keep logged in if succesfull login. Default: True 30 | - **customThrift**: Increase speed thrift with custom thrift. Default: False 31 | - **callback**: Use custom callback. Default: None 32 | - **e2ee**: Use e2ee login. Default: False 33 | :return: 34 | """ 35 | self.certificate = kwargs.pop('certificate', None) 36 | self.systemName = kwargs.pop('systemName', None) 37 | self.appType = kwargs.pop('appType', None) 38 | self.appName = kwargs.pop('appName', None) 39 | self.showQr = kwargs.pop('showQr', False) 40 | self.channelId = kwargs.pop('channelId', None) 41 | self.keepLoggedIn = kwargs.pop('keepLoggedIn', True) 42 | self.customThrift = kwargs.pop('customThrift', False) 43 | self.ignoreSquare = kwargs.pop('ignoreSquare', True) 44 | self.e2ee = kwargs.pop('e2ee', False) 45 | callback = kwargs.pop('callback', None) 46 | if self.e2ee: 47 | self._e2ee = E2EE() 48 | else: 49 | self._e2ee = None 50 | Auth.__init__(self) 51 | if callback and callable(callback): 52 | self.callback = Callback(callback) 53 | if not (idOrAuthToken or idOrAuthToken and passwd): 54 | self.loginWithQrCode() 55 | if idOrAuthToken and passwd: 56 | self.loginWithCredential(idOrAuthToken, passwd) 57 | elif idOrAuthToken and not passwd: 58 | self.loginWithAuthToken(idOrAuthToken) 59 | self.__initAll() 60 | 61 | def __initAll(self): 62 | 63 | self.profile = self.talk.getProfile() 64 | self.userTicket = self.generateUserTicket() 65 | self.groups = self.talk.getGroupIdsJoined() 66 | 67 | Models.__init__(self) 68 | Talk.__init__(self) 69 | Square.__init__(self) 70 | Call.__init__(self) 71 | Timeline.__init__(self) 72 | Liff.__init__(self) 73 | Shop.__init__(self) 74 | -------------------------------------------------------------------------------- /linepy/config.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from akad.ttypes import ApplicationType 3 | import re 4 | 5 | class Config(object): 6 | LINE_HOST_DOMAIN = 'https://gd2.line.naver.jp' 7 | LINE_OBS_DOMAIN = 'https://obs-sg.line-apps.com' 8 | LINE_TIMELINE_API = 'https://gd2.line.naver.jp/mh/api' 9 | LINE_TIMELINE_MH = 'https://gd2.line.naver.jp/mh' 10 | LINE_LIFF_SEND = 'https://api.line.me/message/v3/share' 11 | LINE_PERMISSION_API = 'https://access.line.me/dialog/api/permissions' 12 | 13 | LINE_LOGIN_QUERY_PATH = '/api/v4p/rs' 14 | LINE_AUTH_QUERY_PATH = '/api/v4/TalkService.do' 15 | 16 | LINE_API_QUERY_PATH_FIR = '/S4' 17 | LINE_POLL_QUERY_PATH_FIR = '/P4' 18 | LINE_CALL_QUERY_PATH = '/V4' 19 | LINE_LIFF_QUERY_PATH = '/LIFF1' 20 | LINE_CERTIFICATE_PATH = '/Q' 21 | LINE_CHAN_QUERY_PATH = '/CH4' 22 | LINE_SQUARE_QUERY_PATH = '/SQS1' 23 | LINE_SHOP_QUERY_PATH = '/SHOP4' 24 | 25 | CHANNEL_ID = { 26 | 'HELLO_WORLD': '1602289196', 27 | 'LINE_TIMELINE': '1341209850', 28 | 'LINE_WEBTOON': '1401600689', 29 | 'LINE_TODAY': '1518712866', 30 | 'LINE_STORE': '1376922440', 31 | 'LINE_MUSIC': '1381425814', 32 | 'LINE_SERVICES': '1459630796' 33 | } 34 | 35 | APP_VERSION = { 36 | 'ANDROID': '10.12.1', 37 | 'IOS': '10.12.1', 38 | 'ANDROIDLITE': '2.14.0', 39 | 'DESKTOPWIN': '6.1.1', 40 | 'DESKTOPMAC': '6.1.1', 41 | 'IOSIPAD': '10.12.1', 42 | 'CHROMEOS': '2.3.8', 43 | 'DEFAULT': '10.12.0' 44 | } 45 | 46 | SYSTEM_VERSION = { 47 | 'ANDROID': '10.0', 48 | 'IOS': '13.5.1', 49 | 'ANDROIDLITE': '10.0', 50 | 'DESKTOPWIN': '10.0', 51 | 'DESKTOPMAC': '10.15.1', 52 | 'IOSIPAD': '13.5.1', 53 | 'CHROMEOS': '83.0', 54 | 'DEFAULT': '13.5.1' 55 | } 56 | 57 | APP_TYPE = 'DESKTOPWIN' 58 | APP_VER = APP_VERSION[APP_TYPE] if APP_TYPE in APP_VERSION else APP_VERSION['DEFAULT'] 59 | CARRIER = '51089, 1-0' 60 | SYSTEM_NAME = 'LRTT' 61 | SYSTEM_VER = SYSTEM_VERSION[APP_TYPE] if APP_TYPE in SYSTEM_VERSION else SYSTEM_VERSION['DEFAULT'] 62 | IP_ADDR = '8.8.8.8' 63 | EMAIL_REGEX = re.compile(r"[^@]+@[^@]+\.[^@]+") 64 | URL_REGEX = re.compile(r'^(?:http|ftp)s?://' r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|' r'localhost|' r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' r'(?::\d+)?' r'(?:/?|[/?]\S+)$', re.IGNORECASE) 65 | MID_REGEX = re.compile(r'u[a-f0-9]{32}') 66 | GID_REGEX = re.compile(r'c[a-f0-9]{32}') 67 | RID_REGEX = re.compile(r'r[a-f0-9]{32}') 68 | ALLIDS_REGEX= re.compile(r'[ucr][a-f0-9]{32}') 69 | 70 | def __init__(self, appType=None): 71 | if appType: 72 | self.APP_TYPE = appType 73 | self.APP_VER = self.APP_VERSION[self.APP_TYPE] if self.APP_TYPE in self.APP_VERSION else self.APP_VERSION['DEFAULT'] 74 | self.APP_NAME = '%s\t%s\t%s\t%s' % (self.APP_TYPE, self.APP_VER, self.SYSTEM_NAME, self.SYSTEM_VER) 75 | self.USER_AGENT = 'Line/%s' % self.APP_VER 76 | -------------------------------------------------------------------------------- /linepy/e2ee.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from urllib.parse import quote, unquote, urlencode 3 | from collections import namedtuple 4 | from Crypto.Cipher import AES 5 | import base64, hashlib, os 6 | import axolotl_curve25519 as Curve25519 7 | 8 | KeyPairCurve = namedtuple('KeyPair', ['private_key', 'public_key', 'nonce']) 9 | AESKeyAndIV = namedtuple('AESKey', ['Key', 'IV']) 10 | 11 | class E2EE: 12 | 13 | def __init__(self): 14 | self.Curve = self.generateKeypair() 15 | 16 | def _xor(self, buf): 17 | buf_length = int(len(buf) / 2) 18 | buf2 = bytearray(buf_length) 19 | for i in range(buf_length): 20 | buf2[i] = buf[i] ^ buf[buf_length + i] 21 | return bytes(buf2) 22 | 23 | def _getSHA256Sum(self, *args): 24 | instance = hashlib.sha256() 25 | for arg in args: 26 | if isinstance(arg, str): 27 | arg = arg.encode() 28 | instance.update(arg) 29 | return instance.digest() 30 | 31 | def _encryptAESECB(self, aes_key, plain_data): 32 | aes = AES.new(aes_key, AES.MODE_ECB) 33 | return aes.encrypt(plain_data) 34 | 35 | def _decryptAESECB(self, aes_key, encrypted_data): 36 | aes = AES.new(aes_key, AES.MODE_ECB) 37 | return aes.decrypt(encrypted_data) 38 | 39 | def _encryptAESCBC(self, aes_key, aes_iv, plain_data): 40 | aes = AES.new(aes_key, AES.MODE_CBC, aes_iv) 41 | return aes.encrypt(plain_data) 42 | 43 | def _decrpytAESCBC(self, aes_key, aes_iv, encrypted_data): 44 | aes = AES.new(aes_key, AES.MODE_CBC, aes_iv) 45 | return aes.decrypt(encrypted_data) 46 | 47 | def generateKeypair(self): 48 | private_key = Curve25519.generatePrivateKey(os.urandom(32)) 49 | public_key = Curve25519.generatePublicKey(private_key) 50 | nonce = os.urandom(16) 51 | return KeyPairCurve(private_key, public_key, nonce) 52 | 53 | def generateParams(self): 54 | secret = base64.b64encode(self.Curve.public_key).decode() 55 | return 'secret={secret}&e2eeVersion=1'.format(secret=quote(secret)) 56 | 57 | def generateSharedSecret(self, public_key): 58 | private_key = self.Curve.private_key 59 | shared_secret = Curve25519.calculateAgreement(private_key, public_key) 60 | return shared_secret 61 | 62 | def generateAESKeyAndIV(self, shared_secret): 63 | aes_key = self._getSHA256Sum(shared_secret, 'Key') 64 | aes_iv = self._xor(self._getSHA256Sum(shared_secret, 'IV')) 65 | return AESKeyAndIV(aes_key, aes_iv) 66 | 67 | def generateSignature(self, aes_key, encrypted_data): 68 | data = self._xor(self._getSHA256Sum(encrypted_data)) 69 | signature = self._encryptAESECB(aes_key, data) 70 | return signature 71 | 72 | def verifySignature(self, signature, aes_key, encrypted_data): 73 | data = self._xor(self._getSHA256Sum(encrypted_data)) 74 | return self._decryptAESECB(aes_key, signature) == data 75 | 76 | def decryptKeychain(self, encrypted_keychain, public_key): 77 | public_key = base64.b64decode(public_key) 78 | encrypted_keychain = base64.b64decode(encrypted_keychain) 79 | shared_secret = self.generateSharedSecret(public_key) 80 | aes_key, aes_iv = self.generateAESKeyAndIV(shared_secret) 81 | keychain_data = self._decrpytAESCBC(aes_key, aes_iv, encrypted_keychain) 82 | return keychain_data 83 | -------------------------------------------------------------------------------- /linepy/liff.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from akad.ttypes import ( 3 | LiffViewRequest, 4 | LiffContext, 5 | LiffChatContext, 6 | LiffSquareChatContext, 7 | RevokeTokenRequest, 8 | LiffException, 9 | ) 10 | import requests, json, time 11 | 12 | def loggedIn(func): 13 | def checkLogin(*args, **kwargs): 14 | if args[0].isLogin: 15 | return func(*args, **kwargs) 16 | else: 17 | args[0].callback.default('You want to call the function, you must login to LINE') 18 | return checkLogin 19 | 20 | class Liff: 21 | isLogin = False 22 | liffToken = None 23 | liffTokens = {} 24 | liffBanned = { 25 | 'status': False, 26 | 'time': None 27 | } 28 | wait = 0 29 | 30 | def __init__(self): 31 | self.wait = time.time() 32 | self.isLogin = True 33 | self.resend = False 34 | self.to = None 35 | self.server.setLiffHeadersWithDict({ 36 | 'Authorization': '', 37 | 'Accept': 'application/json, text/plain, */*', 38 | 'User-Agent': 'Mozilla/5.0 (Linux; Android 8.1.0; Mi A1 Build/OPM1.171019.026; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/69.0.3497.91 Mobile Safari/537.36 Line/8.1.1', 39 | 'Accept-Encoding': 'gzip, deflate', 40 | 'Content-Type': 'application/json', 41 | 'X-Requested-With': 'jp.naver.line.android' 42 | }) 43 | try: 44 | self.allowLiff() 45 | except Exception as error: 46 | self.callback.default('Failed allow liff ' + str(error)) 47 | 48 | @loggedIn 49 | def allowLiff(self): 50 | # Copyright by https://github.com/RynKings 51 | data = {'on': ['P', 'CM'], 'off': []} 52 | headers = { 53 | 'X-Line-Access': self.authToken, 54 | 'X-Line-Application': self.server.APP_NAME, 55 | 'X-Line-ChannelId': self.server.CHANNEL_ID['HELLO_WORLD'], 56 | 'Content-Type': 'application/json' 57 | } 58 | r = self.server.postContent(self.server.LINE_PERMISSION_API, headers=headers, data=json.dumps(data)) 59 | return r.json() 60 | 61 | @loggedIn 62 | def issueLiffView(self, to, liffId='1602289196-4xoE1JEr', revokeToken=False, isSquare=False): 63 | if self.liffBanned['status']: 64 | elapsed = time.time() - self.liffBanned['time'] 65 | if elapsed >= 86400: 66 | self.liffBanned['status'] = False 67 | else: 68 | raise Exception('issueLiffView Failed (liffChannel banned please wait untill %i seconds)' % elapsed) 69 | if to in self.liffTokens and not self.liffToken and not revokeToken: 70 | self.liffToken = self.liffTokens[to] 71 | else: 72 | if self.liffToken and revokeToken: 73 | try: 74 | self.revokeToken(self.liffToken) 75 | self.liffToken = None 76 | except Exception: 77 | pass 78 | if isSquare: 79 | context = LiffContext(squareChat=LiffSquareChatContext(to)) 80 | else: 81 | context = LiffContext(chat=LiffChatContext(to)) 82 | liffReq = LiffViewRequest(liffId=liffId, context=context) 83 | try: 84 | liffResp = self.liff.issueLiffView(liffReq) 85 | except LiffException as liff_error: 86 | if liff_error.message == 'invalid request': 87 | self.liffBanned.update({ 88 | 'status': True, 89 | 'time': time.time() 90 | }) 91 | raise Exception('issueLiffView Failed (%s)' % liff_error.message) 92 | except Exception: 93 | raise Exception('issueLiffView Failed (liffId is invalid or your token can\'t do this)') 94 | self.liffToken = liffResp.accessToken 95 | self.liffTokens[to] = self.liffToken 96 | self.to = to 97 | return self.liffToken 98 | 99 | @loggedIn 100 | def sendLiffMessage(self, message, data=None, liffToken=None, revokeToken=False): 101 | if liffToken: 102 | self.server.setLiffHeaders('Authorization', 'Bearer ' + liffToken) 103 | elif self.liffToken: 104 | self.server.setLiffHeaders('Authorization', 'Bearer ' + self.liffToken) 105 | else: 106 | raise Exception('sendLiffMessage Failed (you must issueLiffView before send)') 107 | if not data: 108 | data = {'messages': []} 109 | if isinstance(message, dict): 110 | data['messages'].append(message) 111 | else: 112 | data['messages'] = message 113 | # To avoid liff banned 114 | waiting = self.wait - time.time() 115 | if waiting > 0: time.sleep(waiting) 116 | r = self.server.postContent(self.server.LINE_LIFF_SEND, headers=self.server.liffHeaders, data=json.dumps(data)) 117 | self.wait = time.time() + 1 118 | resp = r.json() 119 | if 'message' in resp and not self.resend and self.to: 120 | self.resend = True 121 | self.issueLiffView(self.to, revokeToken=True) 122 | return self.sendLiffMessage(None, data=data, revokeToken=revokeToken) 123 | if revokeToken: 124 | try: 125 | self.revokeToken(self.liffToken) 126 | self.liffToken = None 127 | except Exception: 128 | pass 129 | self.resend = False 130 | self.to = None 131 | return resp 132 | 133 | @loggedIn 134 | def sendFlexMessage(self, flexContent, altText='Hello World', liffToken=None, revokeToken=False): 135 | if liffToken: 136 | self.server.setLiffHeaders('Authorization', 'Bearer ' + liffToken) 137 | elif self.liffToken: 138 | self.server.setLiffHeaders('Authorization', 'Bearer ' + self.liffToken) 139 | else: 140 | raise Exception('sendLiffMessage Failed (you must issueLiffView before send)') 141 | messages = [ 142 | { 143 | 'type': 'flex', 144 | 'altText': altText, 145 | 'contents': flexContent 146 | } 147 | ] 148 | data = {'messages': messages} 149 | # To avoid liff banned 150 | waiting = self.wait - time.time() 151 | if waiting > 0: time.sleep(waiting) 152 | r = self.server.postContent(self.server.LINE_LIFF_SEND, headers=self.server.liffHeaders, data=json.dumps(data)) 153 | self.wait = time.time() + 1 154 | resp = r.json() 155 | if 'message' in resp and not self.resend and self.to: 156 | self.resend = True 157 | self.issueLiffView(self.to, revokeToken=True) 158 | return self.sendLiffMessage(None, data=data, revokeToken=revokeToken) 159 | if revokeToken: 160 | try: 161 | self.revokeToken(self.liffToken) 162 | self.liffToken = None 163 | except Exception: 164 | pass 165 | self.resend = False 166 | self.to = None 167 | return resp 168 | 169 | @loggedIn 170 | def revokeToken(self, accessToken): 171 | self.server.setLiffHeaders('Authorization', '') 172 | self.liff.revokeToken(RevokeTokenRequest(accessToken)) -------------------------------------------------------------------------------- /linepy/login.py: -------------------------------------------------------------------------------- 1 | from typing import Callable, Dict, Any 2 | from lesting.api.client import build 3 | 4 | class LoginResult: 5 | 6 | certificate: str 7 | accessToken: str 8 | lastBindTimestamp: int 9 | metaData: Dict[str, str] 10 | 11 | class QRLogin: 12 | 13 | HOST = "https://legy-jp.line.naver.jp" 14 | PATH = HOST + "/acct/lgn/sq/v1" 15 | POLL = HOST + "/acct/lp/lgn/sq/v1" 16 | 17 | HEADERS = { 18 | "android_lite": { 19 | "user-agent": "LLA/2.12.0 SKR-H0 9", 20 | "x-line-application": "ANDROIDLITE\t2.12.0\tAndroid OS\t9;SECONDARY" 21 | }, 22 | "android": { 23 | "user-agent": "Line/10.6.2", 24 | "x-line-application": "ANDROID\t10.6.2\tAndroid OS\t10" 25 | }, 26 | "ios_ipad": { 27 | "user-Agent": "Line/10.1.1", 28 | "x-line-application": "IOSIPAD\t10.1.1\tiPhone 8\t11.2.5" 29 | }, 30 | "ios": { 31 | "user-agent": "Line/10.1.1", 32 | "x-line-application": "IOS\t10.1.1\tiPhone 8\t11.2.5" 33 | }, 34 | "chrome": { 35 | "user-agent": "MozilX-Line-Application/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.79 Safari/537.36", 36 | "x-line-application": "CHROMEOS\t2.3.2\tChrome OS\t1" 37 | }, 38 | "desktopwin": { 39 | "user-agent": "Line/5.12.3", 40 | "x-line-application": "DESKTOPWIN\t5.21.3\tWindows\t10" 41 | }, 42 | "desktopmac": { 43 | "user-agent": "Line/5.12.3", 44 | "x-line-application": "DESKTOPMAC\t5.21.3\tMAC\t10.15" 45 | } 46 | } 47 | 48 | def __init__(self) -> None: 49 | self.client = build("line.login", "v1") 50 | 51 | def request(self, method: str, headers: Dict[str, str], *, lp: bool = False, **kwargs: Dict[str, Any]) -> Any: 52 | return getattr(self.client.parser, method)((self.client.http.request((self.POLL if lp else self.PATH), "POST", getattr(self.client.packer, method)(**kwargs), headers)[1])) 53 | 54 | def loginWithQrCode(self, application: str, certificate: str = "", web: bool = False, callback: Callable = lambda output: print(output)) -> LoginResult: 55 | headers = QRLogin.HEADERS[application] 56 | session = self.request("createSession", headers) 57 | result = self.request("createQrCode", headers, session = session) 58 | callback(result.url) 59 | if web: 60 | callback(result.web) 61 | self.request("checkQrCodeVerified", {**headers, **{"x-line-access": session}}, lp = True, session = session) 62 | try: 63 | self.request("verifyCertificate", headers, session = session, certificate = certificate) 64 | except: 65 | pin = self.request("createPinCode", headers, session = session) 66 | if web: 67 | self.client.pin.update(session, pin) 68 | else: 69 | callback(pin) 70 | self.request("checkPinCodeVerified", {**headers, **{"x-line-access": session}}, lp = True, session = session) 71 | return self.request("qrCodeLogin", headers, session = session, systemName = headers["x-line-application"].split("\t")[2], autoLoginIsRequired = True) 72 | 73 | if __name__ == "__main__": 74 | qr = QRLogin() 75 | result = qr.loginWithQrCode("ipad", web = True) 76 | print("Access Token: " + result.accessToken) 77 | print("Certificate: " + result.certificate) 78 | -------------------------------------------------------------------------------- /linepy/models.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from datetime import datetime 3 | from .object import Object 4 | from random import randint 5 | 6 | import json, shutil, time, os, base64, tempfile, re 7 | 8 | class Models(Object): 9 | 10 | def __init__(self): 11 | Object.__init__(self) 12 | 13 | """Text""" 14 | 15 | def log(self, text): 16 | print("[%s] %s" % (str(datetime.now()), text)) 17 | 18 | """File""" 19 | 20 | def saveFile(self, path, raw): 21 | with open(path, 'wb') as f: 22 | shutil.copyfileobj(raw, f) 23 | 24 | def deleteFile(self, path): 25 | if os.path.exists(path): 26 | os.remove(path) 27 | return True 28 | else: 29 | return False 30 | 31 | def downloadFileURL(self, fileUrl, returnAs='path', saveAs='', headers=None): 32 | if returnAs not in ['path','bool','bin']: 33 | raise Exception('Invalid returnAs value') 34 | if saveAs == '': 35 | saveAs = self.genTempFile() 36 | r = self.server.getContent(fileUrl, headers=headers) 37 | if r.status_code != 404: 38 | self.saveFile(saveAs, r.raw) 39 | if returnAs == 'path': 40 | return saveAs 41 | elif returnAs == 'bool': 42 | return True 43 | elif returnAs == 'bin': 44 | return r.raw 45 | else: 46 | raise Exception('Download file failure.') 47 | 48 | """Generator""" 49 | 50 | def validateURL(self, url, returnAs='bool'): 51 | if returnAs not in ['bool', 're']: 52 | raise Exception('Invalid returnAs value') 53 | result = re.match(self.server.URL_REGEX, url) 54 | if returnAs == 'bool': 55 | if result: 56 | return True 57 | else: 58 | return False 59 | return result 60 | 61 | def findMids(self, text): 62 | return self.server.MID_REGEX.findall(text) 63 | 64 | def findGids(self, text): 65 | return self.server.GID_REGEX.findall(text) 66 | 67 | def findRids(self, text): 68 | return self.server.RID_REGEX.findall(text) 69 | 70 | def findAllIds(self, text): 71 | return self.server.ALLIDS_REGEX.findall(text) 72 | 73 | def genTempFile(self, returnAs='path'): 74 | try: 75 | if returnAs not in ['file','path']: 76 | raise Exception('Invalid returnAs value') 77 | fName, fPath = 'linepy-%s-%i.bin' % (int(time.time()), randint(0, 9)), tempfile.gettempdir() 78 | if returnAs == 'file': 79 | return fName 80 | elif returnAs == 'path': 81 | return os.path.join(fPath, fName) 82 | except: 83 | raise Exception('tempfile is required') 84 | 85 | def genOBSParams(self, newList, returnAs='json'): 86 | oldList = {'name': self.genTempFile('file'),'ver': '1.0'} 87 | if returnAs not in ['json','b64','default']: 88 | raise Exception('Invalid parameter returnAs') 89 | if 'name' in newList and not newList['name']: 90 | newList['name'] = oldList['name'] 91 | oldList.update(newList) 92 | if 'range' in oldList: 93 | new_range='bytes 0-%s\/%s' % ( str(oldList['range']-1), str(oldList['range']) ) 94 | oldList.update({'range': new_range}) 95 | if returnAs == 'json': 96 | oldList=json.dumps(oldList) 97 | return oldList 98 | elif returnAs == 'b64': 99 | oldList=json.dumps(oldList) 100 | return base64.b64encode(oldList.encode('utf-8')) 101 | elif returnAs == 'default': 102 | return oldList -------------------------------------------------------------------------------- /linepy/object.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from datetime import datetime 3 | import json, time, ntpath 4 | 5 | def loggedIn(func): 6 | def checkLogin(*args, **kwargs): 7 | if args[0].isLogin: 8 | return func(*args, **kwargs) 9 | else: 10 | args[0].callback.default('You want to call the function, you must login to LINE') 11 | return checkLogin 12 | 13 | class Object(object): 14 | 15 | def __init__(self): 16 | if self.isLogin == True: 17 | self.log("[%s] : Login success" % self.profile.displayName) 18 | 19 | """Group""" 20 | 21 | @loggedIn 22 | def updateGroupPicture(self, groupId, path): 23 | files = {'file': open(path, 'rb')} 24 | data = {'params': self.genOBSParams({'oid': groupId,'type': 'image'})} 25 | r = self.server.postContent(self.server.LINE_OBS_DOMAIN + '/talk/g/upload.nhn', data=data, files=files) 26 | if r.status_code != 201: 27 | raise Exception('Update group picture failure.') 28 | return True 29 | 30 | """Personalize""" 31 | 32 | @loggedIn 33 | def updateProfilePicture(self, path, type='p'): 34 | files = {'file': open(path, 'rb')} 35 | params = {'oid': self.profile.mid,'type': 'image'} 36 | if type == 'vp': 37 | params.update({'ver': '2.0', 'cat': 'vp.mp4'}) 38 | data = {'params': self.genOBSParams(params)} 39 | r = self.server.postContent(self.server.LINE_OBS_DOMAIN + '/talk/p/upload.nhn', data=data, files=files) 40 | if r.status_code != 201: 41 | raise Exception('Update profile picture failure.') 42 | return True 43 | 44 | @loggedIn 45 | def updateProfileVideoPicture(self, path): 46 | try: 47 | from ffmpy import FFmpeg 48 | files = {'file': open(path, 'rb')} 49 | data = {'params': self.genOBSParams({'oid': self.profile.mid,'ver': '2.0','type': 'video','cat': 'vp.mp4'})} 50 | r_vp = self.server.postContent(self.server.LINE_OBS_DOMAIN + '/talk/vp/upload.nhn', data=data, files=files) 51 | if r_vp.status_code != 201: 52 | raise Exception('Update profile video picture failure.') 53 | path_p = self.genTempFile('path') 54 | ff = FFmpeg(inputs={'%s' % path: None}, outputs={'%s' % path_p: ['-ss', '00:00:2', '-vframes', '1']}) 55 | ff.run() 56 | self.updateProfilePicture(path_p, 'vp') 57 | except: 58 | raise Exception('You should install FFmpeg and ffmpy from pypi') 59 | 60 | @loggedIn 61 | def updateVideoAndPictureProfile(self, path_p, path, returnAs='bool'): 62 | if returnAs not in ['bool']: 63 | raise Exception('Invalid returnAs value') 64 | files = {'file': open(path, 'rb')} 65 | data = {'params': self.genOBSParams({'oid': self.profile.mid,'ver': '2.0','type': 'video','cat': 'vp.mp4'})} 66 | r_vp = self.server.postContent(self.server.LINE_OBS_DOMAIN + '/talk/vp/upload.nhn', data=data, files=files) 67 | if r_vp.status_code != 201: 68 | raise Exception('Update profile video picture failure.') 69 | self.updateProfilePicture(path_p, 'vp') 70 | if returnAs == 'bool': 71 | return True 72 | 73 | @loggedIn 74 | def updateProfileCover(self, path, returnAs='bool'): 75 | if returnAs not in ['objId','bool']: 76 | raise Exception('Invalid returnAs value') 77 | objId = self.uploadObjHome(path, type='image', returnAs='objId') 78 | home = self.updateProfileCoverById(objId) 79 | if returnAs == 'objId': 80 | return objId 81 | elif returnAs == 'bool': 82 | return True 83 | 84 | """Object""" 85 | 86 | @loggedIn 87 | def uploadObjSquare(self, squareChatMid, path, type='image', returnAs='bool', name=None): 88 | if returnAs not in ['bool']: 89 | raise Exception('Invalid returnAs value') 90 | if type not in ['image','gif','video','audio','file']: 91 | raise Exception('Invalid type value') 92 | try: 93 | import magic 94 | except ImportError: 95 | raise Exception('You must install python-magic from pip') 96 | mime = magic.Magic(mime=True) 97 | contentType = mime.from_file(path) 98 | data = open(path, 'rb').read() 99 | params = { 100 | 'name': '%s' % str(time.time()*1000), 101 | 'oid': 'reqseq', 102 | 'reqseq': '%s' % str(self.revision), 103 | 'tomid': '%s' % str(squareChatMid), 104 | 'type': '%s' % str(type), 105 | 'ver': '1.0' 106 | } 107 | if type == 'video': 108 | params.update({'duration': '60000'}) 109 | elif type == 'audio': 110 | params.update({'duration': '60000'}) 111 | elif type == 'gif': 112 | params.update({'type': 'image', 'cat': 'original'}) 113 | elif type == 'file': 114 | params.update({'name': name}) 115 | headers = self.server.additionalHeaders(self.server.Headers, { 116 | 'Content-Type': contentType, 117 | 'Content-Length': str(len(data)), 118 | 'x-obs-params': self.genOBSParams(params,'b64'), 119 | 'X-Line-Access': self.squareObsToken 120 | }) 121 | r = self.server.postContent(self.server.LINE_OBS_DOMAIN + '/r/g2/m/reqseq', data=data, headers=headers) 122 | if r.status_code != 201: 123 | raise Exception('Upload %s failure.' % type) 124 | if returnAs == 'bool': 125 | return True 126 | 127 | @loggedIn 128 | def uploadObjTalk(self, path, type='image', returnAs='bool', objId=None, to=None, name=None): 129 | if returnAs not in ['objId','bool']: 130 | raise Exception('Invalid returnAs value') 131 | if type not in ['image','gif','video','audio','file']: 132 | raise Exception('Invalid type value') 133 | headers=None 134 | files = {'file': open(path, 'rb')} 135 | if type == 'image' or type == 'video' or type == 'audio' or type == 'file': 136 | e_p = self.server.LINE_OBS_DOMAIN + '/talk/m/upload.nhn' 137 | data = {'params': self.genOBSParams({'oid': objId,'size': len(open(path, 'rb').read()),'type': type, 'name': name})} 138 | elif type == 'gif': 139 | e_p = self.server.LINE_OBS_DOMAIN + '/r/talk/m/reqseq' 140 | files = None 141 | data = open(path, 'rb').read() 142 | params = { 143 | 'name': '%s' % str(time.time()*1000), 144 | 'oid': 'reqseq', 145 | 'reqseq': '%s' % str(self.revision), 146 | 'tomid': '%s' % str(to), 147 | 'cat': 'original', 148 | 'type': 'image', 149 | 'ver': '1.0' 150 | } 151 | headers = self.server.additionalHeaders(self.server.Headers, { 152 | 'Content-Type': 'image/gif', 153 | 'Content-Length': str(len(data)), 154 | 'x-obs-params': self.genOBSParams(params,'b64') 155 | }) 156 | r = self.server.postContent(e_p, data=data, headers=headers, files=files) 157 | if r.status_code != 201: 158 | raise Exception('Upload %s failure.' % type) 159 | if returnAs == 'objId': 160 | return objId 161 | elif returnAs == 'bool': 162 | return True 163 | 164 | @loggedIn 165 | def uploadObjHome(self, path, type='image', returnAs='bool', objId=None): 166 | if returnAs not in ['objId','bool']: 167 | raise Exception('Invalid returnAs value') 168 | if type not in ['image','video','audio']: 169 | raise Exception('Invalid type value') 170 | if type == 'image': 171 | contentType = 'image/jpeg' 172 | elif type == 'video': 173 | contentType = 'video/mp4' 174 | elif type == 'audio': 175 | contentType = 'audio/mp3' 176 | if not objId: 177 | objId = int(time.time()) 178 | file = open(path, 'rb').read() 179 | params = { 180 | 'name': '%s' % str(time.time()*1000), 181 | 'userid': '%s' % self.profile.mid, 182 | 'oid': '%s' % str(objId), 183 | 'type': type, 184 | 'ver': '1.0' 185 | } 186 | hr = self.server.additionalHeaders(self.server.timelineHeaders, { 187 | 'Content-Type': contentType, 188 | 'Content-Length': str(len(file)), 189 | 'x-obs-params': self.genOBSParams(params,'b64') 190 | }) 191 | r = self.server.postContent(self.server.LINE_OBS_DOMAIN + '/myhome/c/upload.nhn', headers=hr, data=file) 192 | if r.status_code != 201: 193 | raise Exception('Upload object home failure.') 194 | if returnAs == 'objId': 195 | return objId 196 | elif returnAs == 'bool': 197 | return True 198 | 199 | @loggedIn 200 | def downloadObjectMsg(self, messageId, returnAs='path', saveAs=''): 201 | if saveAs == '': 202 | saveAs = self.genTempFile('path') 203 | if returnAs not in ['path','bool','bin']: 204 | raise Exception('Invalid returnAs value') 205 | params = {'oid': messageId} 206 | url = self.server.urlEncode(self.server.LINE_OBS_DOMAIN, '/talk/m/download.nhn', params) 207 | r = self.server.getContent(url) 208 | if r.status_code == 200: 209 | self.saveFile(saveAs, r.raw) 210 | if returnAs == 'path': 211 | return saveAs 212 | elif returnAs == 'bool': 213 | return True 214 | elif returnAs == 'bin': 215 | return r.raw 216 | else: 217 | raise Exception('Download object failure.') 218 | 219 | @loggedIn 220 | def forwardObjectMsg(self, to, msgId, contentType='image'): 221 | if contentType not in ['image','video','audio']: 222 | raise Exception('Type not valid.') 223 | data = self.genOBSParams({'oid': 'reqseq','reqseq': self.revision,'type': contentType,'copyFrom': '/talk/m/%s' % msgId},'default') 224 | r = self.server.postContent(self.server.LINE_OBS_DOMAIN + '/talk/m/copy.nhn', data=data) 225 | if r.status_code != 200: 226 | raise Exception('Forward object failure.') 227 | return True 228 | -------------------------------------------------------------------------------- /linepy/oepoll.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from akad.ttypes import TalkException, ShouldSyncException 3 | from .client import LINE 4 | from threading import Thread 5 | from types import * 6 | 7 | import os, sys, time 8 | 9 | class OEPoll(object): 10 | OpInterrupt = {} 11 | client = None 12 | __squareSubId = {} 13 | __squareSyncToken = {} 14 | 15 | def __init__(self, client): 16 | if type(client) is not LINE: 17 | raise Exception('You need to set LINE instance to initialize OEPoll') 18 | self.client = client 19 | self.threads = [] 20 | 21 | def __execute(self, op, threading): 22 | try: 23 | if threading: 24 | _td = Thread(target=self.OpInterrupt[op.type], args=(op,)) 25 | _td.daemon = False 26 | self.threads.append(_td) 27 | else: 28 | self.OpInterrupt[op.type](op) 29 | except Exception as e: 30 | self.client.log(e) 31 | 32 | def addOpInterruptWithDict(self, OpInterruptDict): 33 | self.OpInterrupt.update(OpInterruptDict) 34 | 35 | def addOpInterrupt(self, OperationType, DisposeFunc): 36 | self.OpInterrupt[OperationType] = DisposeFunc 37 | 38 | def setRevision(self, revision): 39 | self.client.revision = max(revision, self.client.revision) 40 | 41 | def singleTrace(self, count=1, fetchOperations=None): 42 | if not fetchOperations: 43 | fetchOperations = self.client.fetchOperation 44 | try: 45 | operations = fetchOperations(self.client.revision, count=count) 46 | except KeyboardInterrupt: 47 | sys.exit() 48 | except ShouldSyncException: 49 | self.setRevision(self.client.poll.getLastOpRevision()) 50 | return [] 51 | except: 52 | return [] 53 | 54 | if operations is None: 55 | return [] 56 | else: 57 | return operations 58 | 59 | def trace(self, threading=False, fetchOperations=None): 60 | if not fetchOperations: 61 | fetchOperations = self.client.fetchOperation 62 | try: 63 | operations = fetchOperations(self.client.revision) 64 | except KeyboardInterrupt: 65 | sys.exit() 66 | except ShouldSyncException: 67 | self.setRevision(self.client.poll.getLastOpRevision()) 68 | return 69 | except: 70 | return 71 | 72 | for op in operations: 73 | if op.type in self.OpInterrupt.keys(): 74 | self.__execute(op, threading) 75 | self.setRevision(op.revision) 76 | for thread in self.threads: 77 | thread.start() 78 | for thread in self.threads: 79 | thread.join() 80 | self.threads = [] 81 | 82 | def singleFetchSquareChat(self, squareChatMid, limit=1): 83 | if squareChatMid not in self.__squareSubId: 84 | self.__squareSubId[squareChatMid] = 0 85 | if squareChatMid not in self.__squareSyncToken: 86 | self.__squareSyncToken[squareChatMid] = '' 87 | 88 | sqcEvents = self.client.fetchSquareChatEvents(squareChatMid, subscriptionId=self.__squareSubId[squareChatMid], syncToken=self.__squareSyncToken[squareChatMid], limit=limit, direction=1) 89 | self.__squareSubId[squareChatMid] = sqcEvents.subscription 90 | self.__squareSyncToken[squareChatMid] = sqcEvents.syncToken 91 | 92 | return sqcEvents.events -------------------------------------------------------------------------------- /linepy/server.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from .config import Config 3 | import json, requests, urllib 4 | 5 | class Server(Config): 6 | _session = requests.session() 7 | timelineHeaders = {} 8 | liffHeaders = {} 9 | Headers = {} 10 | 11 | def __init__(self, appType=None): 12 | self.Headers = {} 13 | self.timelineHeaders = {} 14 | Config.__init__(self, appType) 15 | 16 | def parseUrl(self, path): 17 | return self.LINE_HOST_DOMAIN + path 18 | 19 | def urlEncode(self, url, path, params=[]): 20 | return url + path + '?' + urllib.parse.urlencode(params) 21 | 22 | def getJson(self, url, allowHeader=False): 23 | if allowHeader is False: 24 | return json.loads(self._session.get(url).text) 25 | else: 26 | return json.loads(self._session.get(url, headers=self.Headers).text) 27 | 28 | def setHeadersWithDict(self, headersDict): 29 | self.Headers.update(headersDict) 30 | 31 | def setHeaders(self, argument, value): 32 | self.Headers[argument] = value 33 | 34 | def setTimelineHeadersWithDict(self, headersDict): 35 | self.timelineHeaders.update(headersDict) 36 | 37 | def setTimelineHeaders(self, argument, value): 38 | self.timelineHeaders[argument] = value 39 | 40 | def setLiffHeadersWithDict(self, headersDict): 41 | self.liffHeaders.update(headersDict) 42 | 43 | def setLiffHeaders(self, key, value): 44 | self.liffHeaders[key] = value 45 | 46 | def additionalHeaders(self, source, newSource): 47 | headerList={} 48 | headerList.update(source) 49 | headerList.update(newSource) 50 | return headerList 51 | 52 | def optionsContent(self, url, data=None, headers=None): 53 | if headers is None: 54 | headers=self.Headers 55 | return self._session.options(url, headers=headers, data=data) 56 | 57 | def postContent(self, url, data=None, files=None, headers=None): 58 | if headers is None: 59 | headers=self.Headers 60 | return self._session.post(url, headers=headers, data=data, files=files) 61 | 62 | def getContent(self, url, headers=None): 63 | if headers is None: 64 | headers=self.Headers 65 | return self._session.get(url, headers=headers, stream=True) 66 | 67 | def deleteContent(self, url, data=None, headers=None): 68 | if headers is None: 69 | headers=self.Headers 70 | return self._session.delete(url, headers=headers, data=data) 71 | 72 | def putContent(self, url, data=None, headers=None): 73 | if headers is None: 74 | headers=self.Headers 75 | return self._session.put(url, headers=headers, data=data) 76 | -------------------------------------------------------------------------------- /linepy/session.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from .transport import THttpClient 3 | from thrift.protocol import TCompactProtocol 4 | from akad import AuthService, TalkService, ChannelService, CallService, SquareService, LiffService, ShopService 5 | 6 | class Session: 7 | 8 | def __init__(self, url, headers, path='', customThrift=False): 9 | self.host = url + path 10 | self.headers = headers 11 | self.customThrift = customThrift 12 | 13 | def Auth(self, isopen=True): 14 | self.transport = THttpClient(self.host, customThrift=self.customThrift) 15 | self.transport.setCustomHeaders(self.headers) 16 | 17 | self.protocol = TCompactProtocol.TCompactProtocol(self.transport) 18 | self._auth = AuthService.Client(self.protocol) 19 | 20 | if isopen: 21 | self.transport.open() 22 | 23 | return self._auth 24 | 25 | def Talk(self, isopen=True): 26 | self.transport = THttpClient(self.host, customThrift=self.customThrift) 27 | self.transport.setCustomHeaders(self.headers) 28 | 29 | self.protocol = TCompactProtocol.TCompactProtocol(self.transport) 30 | self._talk = TalkService.Client(self.protocol) 31 | if isopen: 32 | self.transport.open() 33 | 34 | return self._talk 35 | 36 | def Channel(self, isopen=True): 37 | self.transport = THttpClient(self.host, customThrift=self.customThrift) 38 | self.transport.setCustomHeaders(self.headers) 39 | 40 | self.protocol = TCompactProtocol.TCompactProtocol(self.transport) 41 | self._channel = ChannelService.Client(self.protocol) 42 | 43 | if isopen: 44 | self.transport.open() 45 | 46 | return self._channel 47 | 48 | def Call(self, isopen=True): 49 | self.transport = THttpClient(self.host, customThrift=self.customThrift) 50 | self.transport.setCustomHeaders(self.headers) 51 | 52 | self.protocol = TCompactProtocol.TCompactProtocol(self.transport) 53 | self._call = CallService.Client(self.protocol) 54 | 55 | if isopen: 56 | self.transport.open() 57 | 58 | return self._call 59 | 60 | def Square(self, isopen=True): 61 | self.transport = THttpClient(self.host, customThrift=self.customThrift) 62 | self.transport.setCustomHeaders(self.headers) 63 | 64 | self.protocol = TCompactProtocol.TCompactProtocol(self.transport) 65 | self._square = SquareService.Client(self.protocol) 66 | 67 | if isopen: 68 | self.transport.open() 69 | 70 | return self._square 71 | 72 | def Shop(self, isopen=True): 73 | self.transport = THttpClient(self.host, customThrift=self.customThrift) 74 | self.transport.setCustomHeaders(self.headers) 75 | 76 | self.protocol = TCompactProtocol.TCompactProtocol(self.transport) 77 | self._shop = ShopService.Client(self.protocol) 78 | 79 | if isopen: 80 | self.transport.open() 81 | 82 | return self._shop 83 | 84 | def Liff(self, isopen=True): 85 | self.transport = THttpClient(self.host, customThrift=self.customThrift) 86 | self.transport.setCustomHeaders(self.headers) 87 | 88 | self.protocol = TCompactProtocol.TCompactProtocol(self.transport) 89 | self._liff = LiffService.Client(self.protocol) 90 | 91 | if isopen: 92 | self.transport.open() 93 | 94 | return self._liff 95 | -------------------------------------------------------------------------------- /linepy/shop.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | def loggedIn(func): 4 | def checkLogin(*args, **kwargs): 5 | if args[0].isLogin: 6 | return func(*args, **kwargs) 7 | else: 8 | args[0].callback.default('You must login to LINE') 9 | return checkLogin 10 | 11 | class Shop(object): 12 | isLogin = False 13 | 14 | def __init__(self): 15 | self.isLogin = True 16 | 17 | @loggedIn 18 | def getProduct(self, packageID, language, country): 19 | return self.shop.getProduct(packageID, language, country) 20 | 21 | @loggedIn 22 | def getActivePurchases(self, start, size, language, country): 23 | return self.shop.getActivePurchases(start, size, language, country) 24 | -------------------------------------------------------------------------------- /linepy/square.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from akad.ttypes import * 3 | from random import randint 4 | 5 | def loggedIn(func): 6 | def checkLogin(*args, **kwargs): 7 | if args[0].isSupportSquare: 8 | if args[0].isLogin: 9 | return func(*args, **kwargs) 10 | else: 11 | args[0].callback.default('You want to call the function, you must login to LINE') 12 | else: 13 | args[0].callback.default('Your LINE account doesn\'t support Square') 14 | return checkLogin 15 | 16 | class Square(object): 17 | isSupportSquare = False 18 | isLogin = False 19 | 20 | def __init__(self): 21 | self.isLogin = True 22 | try: 23 | if self.ignoreSquare: 24 | self.isSupportSquare = False 25 | self.log('Square login ignored') 26 | else: 27 | self.isSupportSquare = True 28 | self.squares = self.getJoinedSquares().squares 29 | self.squareObsToken = self.acquireEncryptedAccessToken(2).split('\x1e')[1] 30 | except: 31 | self.isSupportSquare = False 32 | self.log('Your LINE account doesn\'t support Square') 33 | 34 | """Object""" 35 | 36 | @loggedIn 37 | def sendSquareImage(self, squareChatMid, path): 38 | return self.uploadObjSquare(squareChatMid=squareChatMid, path=path, type='image', returnAs='bool') 39 | 40 | @loggedIn 41 | def sendSquareImageWithURL(self, squareChatMid, url): 42 | path = self.downloadFileURL(url, 'path') 43 | return self.sendSquareImage(squareChatMid, path) 44 | 45 | @loggedIn 46 | def sendSquareGIF(self, squareChatMid, path): 47 | return self.uploadObjSquare(squareChatMid=squareChatMid, path=path, type='gif', returnAs='bool') 48 | 49 | @loggedIn 50 | def sendSquareGIFWithURL(self, squareChatMid, url): 51 | path = self.downloadFileURL(url, 'path') 52 | return self.sendSquareGIF(squareChatMid, path) 53 | 54 | @loggedIn 55 | def sendSquareVideo(self, squareChatMid, path): 56 | return self.uploadObjSquare(squareChatMid=squareChatMid, path=path, type='video', returnAs='bool') 57 | 58 | @loggedIn 59 | def sendSquareVideoWithURL(self, squareChatMid, url): 60 | path = self.downloadFileURL(url, 'path') 61 | return self.sendSquareVideo(squareChatMid, path) 62 | 63 | @loggedIn 64 | def sendSquareAudio(self, squareChatMid, path): 65 | return self.uploadObjSquare(squareChatMid=squareChatMid, path=path, type='audio', returnAs='bool') 66 | 67 | @loggedIn 68 | def sendSquareAudioWithURL(self, squareChatMid, url): 69 | path = self.downloadFileURL(url, 'path') 70 | return self.sendSquareAudio(squareChatMid, path) 71 | 72 | @loggedIn 73 | def sendSquareFile(self, squareChatMid, path, filename=''): 74 | return self.uploadObjSquare(squareChatMid=squareChatMid, path=path, type='file', returnAs='bool', name=filename) 75 | 76 | @loggedIn 77 | def sendSquareFileWithURL(self, squareChatMid, url, filename=''): 78 | path = self.downloadFileURL(url, 'path') 79 | return self.sendSquareFile(squareChatMid, path, filename) 80 | 81 | """Square Message""" 82 | 83 | @loggedIn 84 | def sendSquareMessage(self, squareChatMid, text, contentMetadata={}, contentType=0): 85 | rq = SendMessageRequest() 86 | rq.squareChatMid = squareChatMid 87 | rq.squareMessage = SquareMessage() 88 | msg = Message() 89 | msg.to = squareChatMid 90 | msg.text = text 91 | msg.contentType, msg.contentMetadata = contentType, contentMetadata 92 | rq.squareMessage.message = msg 93 | rq.squareMessage.fromType = 4 94 | if squareChatMid not in self._messageReq: 95 | self._messageReq[squareChatMid] = -1 96 | self._messageReq[squareChatMid] += 1 97 | rq.squareMessage.squareMessageRevision = self._messageReq[squareChatMid] 98 | return self.square.sendMessage(rq) 99 | 100 | @loggedIn 101 | def sendSquareMessageWithFooter(self, squareChatMid, text, title=None, link=None, iconlink=None, contentMetadata={}): 102 | rq = SendMessageRequest() 103 | rq.squareChatMid = squareChatMid 104 | rq.squareMessage = SquareMessage() 105 | msg = Message() 106 | msg.to = squareChatMid 107 | msg.text = text 108 | msg.contentType = 0 109 | msg.contentMetadata = self.generateMessageFooter(title, link, iconlink) 110 | if contentMetadata: 111 | msg.contentMetadata.update(contentMetadata) 112 | rq.squareMessage.message = msg 113 | rq.squareMessage.fromType = 4 114 | if squareChatMid not in self._messageReq: 115 | self._messageReq[squareChatMid] = -1 116 | self._messageReq[squareChatMid] += 1 117 | rq.squareMessage.squareMessageRevision = self._messageReq[squareChatMid] 118 | return self.square.sendMessage(rq) 119 | 120 | @loggedIn 121 | def sendSquareReplyMessage(self, relatedMessageId, squareChatMid, text, contentMetadata={}, contentType=0): 122 | rq = SendMessageRequest() 123 | rq.squareChatMid = squareChatMid 124 | rq.squareMessage = SquareMessage() 125 | msg = self.generateReplyMessage(relatedMessageId) 126 | msg.to = squareChatMid 127 | msg.text = text 128 | msg.contentType = contentType 129 | msg.contentMetadata = contentMetadata 130 | msg.relatedMessageServiceCode = 2 131 | rq.squareMessage.message = msg 132 | rq.squareMessage.fromType = 4 133 | if squareChatMid not in self._messageReq: 134 | self._messageReq[squareChatMid] = -1 135 | self._messageReq[squareChatMid] += 1 136 | rq.squareMessage.squareMessageRevision = self._messageReq[squareChatMid] 137 | return self.square.sendMessage(rq) 138 | 139 | @loggedIn 140 | def sendSquareMention(self, to, mid, firstmessage='', lastmessage=''): 141 | arrData = "" 142 | text = "%s " %(str(firstmessage)) 143 | arr = [] 144 | mention = "@zeroxyuuki " 145 | slen = str(len(text)) 146 | elen = str(len(text) + len(mention) - 1) 147 | arrData = {'S':slen, 'E':elen, 'M':mid} 148 | arr.append(arrData) 149 | text += mention + str(lastmessage) 150 | self.sendSquareMessage(to, text, {'MENTION': str('{"MENTIONEES":' + json.dumps(arr) + '}')}, 0) 151 | 152 | @loggedIn 153 | def sendSquareMentionV2(self, to, text="", mids=[], isUnicode=False): 154 | arrData = "" 155 | arr = [] 156 | mention = "@zeroxyuuki " 157 | if mids == []: 158 | raise Exception("Invalid mids") 159 | if "@!" in text: 160 | if text.count("@!") != len(mids): 161 | raise Exception("Invalid mids") 162 | texts = text.split("@!") 163 | textx = "" 164 | unicode = "" 165 | if isUnicode: 166 | for mid in mids: 167 | unicode += str(texts[mids.index(mid)].encode('unicode-escape')) 168 | textx += str(texts[mids.index(mid)]) 169 | slen = len(textx) if unicode == textx else len(textx) + unicode.count('U0') 170 | elen = len(textx) + 15 171 | arrData = {'S':str(slen), 'E':str(elen - 4), 'M':mid} 172 | arr.append(arrData) 173 | textx += mention 174 | else: 175 | for mid in mids: 176 | textx += str(texts[mids.index(mid)]) 177 | slen = len(textx) 178 | elen = len(textx) + 15 179 | arrData = {'S':str(slen), 'E':str(elen - 4), 'M':mid} 180 | arr.append(arrData) 181 | textx += mention 182 | textx += str(texts[len(mids)]) 183 | else: 184 | raise Exception("Invalid mention position") 185 | self.sendSquareMessage(to, textx, {'MENTION': str('{"MENTIONEES":' + json.dumps(arr) + '}')}, 0) 186 | 187 | @loggedIn 188 | def sendSquareSticker(self, squareChatMid, packageId, stickerId): 189 | contentMetadata = { 190 | 'STKVER': '100', 191 | 'STKPKGID': packageId, 192 | 'STKID': stickerId 193 | } 194 | return self.sendSquareMessage(squareChatMid, '', contentMetadata, 7) 195 | 196 | @loggedIn 197 | def sendSquareContact(self, squareChatMid, mid): 198 | contentMetadata = {'mid': mid} 199 | return self.sendSquareMessage(squareChatMid, '', contentMetadata, 13) 200 | 201 | @loggedIn 202 | def sendSquareGift(self, squareChatMid, productId, productType): 203 | if productType not in ['theme','sticker']: 204 | raise Exception('Invalid productType value') 205 | contentMetadata = { 206 | 'MSGTPL': str(randint(0, 10)), 207 | 'PRDTYPE': productType.upper(), 208 | 'STKPKGID' if productType == 'sticker' else 'PRDID': productId 209 | } 210 | return self.sendSquareMessage(squareChatMid, '', contentMetadata, 9) 211 | 212 | @loggedIn 213 | def destroySquareMessage(self, squareChatMid, messageId): 214 | rq = DestroyMessageRequest() 215 | rq.squareChatMid = squareChatMid 216 | rq.messageId = messageId 217 | return self.square.destroyMessage(rq) 218 | 219 | """Square""" 220 | 221 | @loggedIn 222 | def searchSquareMembers(self, squareMid, continuationToken=None, limit=50): 223 | rq = SearchSquareMembersRequest() 224 | rq.squareMid = squareMid 225 | rq.searchOption = SquareMemberSearchOption() 226 | rq.continuationToken = continuationToken 227 | rq.limit = limit 228 | return self.square.searchSquareMembers(rq) 229 | 230 | @loggedIn 231 | def findSquareByInvitationTicket(self, invitationTicket): 232 | rq = FindSquareByInvitationTicketRequest() 233 | rq.invitationTicket = invitationTicket 234 | return self.square.findSquareByInvitationTicket(rq) 235 | 236 | @loggedIn 237 | def approveSquareMembers(self, squareMid, requestedMemberMids=[]): 238 | rq = ApproveSquareMembersRequest() 239 | rq.squareMid = squareMid 240 | rq.requestedMemberMids = requestedMemberMids 241 | return self.square.approveSquareMembers(rq) 242 | 243 | @loggedIn 244 | def deleteSquare(self, mid): 245 | rq = DeleteSquareRequest() 246 | rq.mid = mid 247 | rq.revision = self.revision 248 | return self.square.deleteSquare(rq) 249 | 250 | @loggedIn 251 | def deleteSquareChat(self, squareChatMid): 252 | rq = DeleteSquareChatRequest() 253 | rq.squareChatMid = squareChatMid 254 | rq.revision = self.revision 255 | return self.square.deleteSquareChat(request) 256 | 257 | @loggedIn 258 | def createSquare(self, name, categoryID, welcomeMessage='', profileImageObsHash='', desc='', searchable=True, type=1, ableToUseInvitationTicket=True): 259 | rq = CreateSquareRequest() 260 | rq.square = Square() 261 | rq.square.name = name 262 | rq.square.categoryID = categoryID 263 | rq.square.welcomeMessage = welcomeMessage 264 | rq.square.profileImageObsHash = profileImageObsHash 265 | rq.square.desc = desc 266 | rq.square.searchable = searchable 267 | rq.square.type = type 268 | rq.square.ableToUseInvitationTicket = ableToUseInvitationTicket 269 | rq.creator = SquareMember() 270 | return self.square.createSquare(rq) 271 | 272 | @loggedIn 273 | def createSquareChat(self, squareMid, name, squareMemberMids): 274 | rq = CreateSquareChatRequest() 275 | rq.reqSeq = self.revision 276 | rq.squareChat = SquareChat() 277 | rq.squareChat.squareMid = squareMid 278 | rq.squareChat.name = name 279 | rq.squareMemberMids = squareMemberMids 280 | return self.square.createSquareChat(request) 281 | 282 | @loggedIn 283 | def fetchSquareChatEvents(self, squareChatMid, subscriptionId=0, syncToken='', limit=50, direction=2): 284 | rq = FetchSquareChatEventsRequest() 285 | rq.squareChatMid = squareChatMid 286 | rq.subscriptionId = subscriptionId 287 | rq.syncToken = syncToken 288 | rq.limit = limit 289 | rq.direction = direction 290 | return self.square.fetchSquareChatEvents(rq) 291 | 292 | @loggedIn 293 | def fetchMyEvents(self, subscriptionId=0, syncToken='', continuationToken=None, limit=50): 294 | rq = FetchMyEventsRequest() 295 | rq.subscriptionId = subscriptionId 296 | rq.syncToken = syncToken 297 | rq.continuationToken = continuationToken 298 | rq.limit = limit 299 | return self.square.fetchMyEvents(rq) 300 | 301 | @loggedIn 302 | def markAsRead(self, squareChatMid, messageId): 303 | rq = MarkAsReadRequest() 304 | rq.squareChatMid = squareChatMid 305 | rq.messageId = messageId 306 | return self.square.markAsRead(rq) 307 | 308 | @loggedIn 309 | def getSquareAuthority(self, squareMid): 310 | rq = GetSquareAuthorityRequest() 311 | rq.squareMid = squareMid 312 | return self.square.getSquareAuthority(rq) 313 | 314 | @loggedIn 315 | def leaveSquare(self, squareMid): 316 | rq = LeaveSquareRequest() 317 | rq.squareMid = squareMid 318 | return self.square.leaveSquare(rq) 319 | 320 | @loggedIn 321 | def leaveSquareChat(self, squareChatMid, squareChatMemberRevision, sayGoodbye=True): 322 | rq = LeaveSquareChatRequest() 323 | rq.squareChatMid = squareChatMid 324 | rq.sayGoodbye = sayGoodbye 325 | rq.squareChatMemberRevision = squareChatMemberRevision 326 | return self.square.leaveSquareChat(rq) 327 | 328 | @loggedIn 329 | def joinSquareChat(self, squareChatMid): 330 | rq = JoinSquareChatRequest() 331 | rq.squareChatMid = squareChatMid 332 | return self.square.joinSquareChat(rq) 333 | 334 | @loggedIn 335 | def joinSquare(self, squareMid, displayName, profileImageObsHash): 336 | rq = JoinSquareRequest() 337 | rq.squareMid = squareMid 338 | rq.member = SquareMember() 339 | rq.member.squareMid = squareMid 340 | rq.member.displayName = displayName 341 | rq.member.profileImageObsHash = profileImageObsHash 342 | return self.square.joinSquare(rq) 343 | 344 | @loggedIn 345 | def inviteToSquare(self, squareMid, squareChatMid, invitees=[]): 346 | rq = InviteToSquareRequest() 347 | rq.squareMid = squareMid 348 | rq.invitees = invitees 349 | rq.squareChatMid = squareChatMid 350 | return self.square.inviteToSquare(rq) 351 | 352 | @loggedIn 353 | def inviteToSquareChat(self, squareChatMid, inviteeMids=[]): 354 | rq = InviteToSquareChatRequest() 355 | rq.inviteeMids = inviteeMids 356 | rq.squareChatMid = squareChatMid 357 | return self.square.inviteToSquareChat(rq) 358 | 359 | @loggedIn 360 | def getSquareMember(self, squareMemberMid): 361 | rq = GetSquareMemberRequest() 362 | rq.squareMemberMid = squareMemberMid 363 | return self.square.getSquareMember(rq) 364 | 365 | @loggedIn 366 | def getSquareMembers(self, mids=[]): 367 | rq = GetSquareMembersRequest() 368 | rq.mids = mids 369 | return self.square.getSquareMembers(rq) 370 | 371 | @loggedIn 372 | def getSquareMemberRelation(self, squareMid, targetSquareMemberMid): 373 | rq = GetSquareMemberRelationRequest() 374 | rq.squareMid = squareMid 375 | rq.targetSquareMemberMid = targetSquareMemberMid 376 | return self.square.getSquareMemberRelation(rq) 377 | 378 | @loggedIn 379 | def getSquareMemberRelations(self, state=1, continuationToken=None, limit=50): 380 | rq = GetSquareMemberRelationsRequest() 381 | rq.state = state # 1 NONE, 2 BLOCKED 382 | rq.continuationToken = continuationToken 383 | rq.limit = limit 384 | return self.square.getSquareMemberRelations(rq) 385 | 386 | @loggedIn 387 | def getSquareChatMembers(self, squareChatMid, continuationToken=None, limit=50): 388 | rq = GetSquareChatMembersRequest() 389 | rq.squareChatMid = squareChatMid 390 | rq.continuationToken = continuationToken 391 | rq.limit = limit 392 | return self.square.getSquareChatMembers(rq) 393 | 394 | @loggedIn 395 | def getSquareChatStatus(self, squareChatMid): 396 | rq = GetSquareChatStatusRequest() 397 | rq.squareChatMid = squareChatMid 398 | return self.square.getSquareChatStatus(rq) 399 | 400 | @loggedIn 401 | def getSquareChat(self, squareChatMid): 402 | rq = GetSquareChatRequest() 403 | rq.squareChatMid = squareChatMid 404 | return self.square.getSquareChat(rq) 405 | 406 | @loggedIn 407 | def getSquare(self, mid): 408 | rq = GetSquareRequest() 409 | rq.mid = mid 410 | return self.square.getSquare(rq) 411 | 412 | @loggedIn 413 | def getSquareChatAnnouncements(self, squareChatMid): 414 | rq = GetSquareChatAnnouncementsRequest() 415 | rq.squareChatMid = squareChatMid 416 | return self.square.getSquareChatAnnouncements(rq) 417 | 418 | @loggedIn 419 | def deleteSquareChatAnnouncement(self, squareChatMid, announcementSeq): 420 | rq = DeleteSquareChatAnnouncementRequest() 421 | rq.squareChatMid = squareChatMid 422 | rq.squareChatMid = announcementSeq 423 | return self.square.deleteSquareChatAnnouncement(rq) 424 | 425 | @loggedIn 426 | def createSquareChatAnnouncement(self, squareChatMid, text, messageId='', senderSquareMemberMid=''): 427 | rq = CreateSquareChatAnnouncementRequest() 428 | rq.reqSeq = 0 429 | rq.squareChatMid = squareChatMid 430 | rq.squareChatAnnouncement = SquareChatAnnouncement() 431 | rq.squareChatAnnouncement.announcementSeq = 0 432 | rq.squareChatAnnouncement.type = 0 433 | rq.squareChatAnnouncement.contents = SquareChatAnnouncementContents() 434 | rq.squareChatAnnouncement.contents.textMessageAnnouncementContents = TextMessageAnnouncementContents() 435 | rq.squareChatAnnouncement.contents.textMessageAnnouncementContents.messageId = messageId 436 | rq.squareChatAnnouncement.contents.textMessageAnnouncementContents.text = text 437 | rq.squareChatAnnouncement.contents.textMessageAnnouncementContents.senderSquareMemberMid = senderSquareMemberMid 438 | return self.square.createSquareChatAnnouncement(rq) 439 | 440 | @loggedIn 441 | def getJoinedSquares(self, continuationToken=None, limit=50): 442 | rq = GetJoinedSquaresRequest() 443 | rq.continuationToken = continuationToken 444 | rq.limit = limit 445 | return self.square.getJoinedSquares(rq) 446 | 447 | @loggedIn 448 | def getJoinedSquareChats(self, continuationToken=None, limit=50): 449 | rq = GetJoinedSquareChatsRequest() 450 | rq.continuationToken = continuationToken 451 | rq.limit = limit 452 | return self.square.getJoinedSquareChats(rq) 453 | 454 | @loggedIn 455 | def getJoinableSquareChats(self, squareMid, continuationToken=None, limit=50): 456 | rq = GetJoinableSquareChatsRequest() 457 | rq.squareMid = squareMid 458 | rq.continuationToken = continuationToken 459 | rq.limit = limit 460 | return self.square.getJoinableSquareChats(rq) 461 | 462 | @loggedIn 463 | def getInvitationTicketUrl(self, mid): 464 | rq = GetInvitationTicketUrlRequest() 465 | rq.mid = mid 466 | return self.square.getInvitationTicketUrl(rq) 467 | 468 | @loggedIn 469 | def getSquareStatus(self, squareMid): 470 | rq = GetSquareStatusRequest() 471 | rq.squareMid = squareMid 472 | return self.square.getSquareStatus(rq) 473 | 474 | @loggedIn 475 | def getNoteStatus(self, squareMid): 476 | rq = GetNoteStatusRequest() 477 | rq.squareMid = squareMid 478 | return self.square.getNoteStatus(rq) 479 | 480 | @loggedIn 481 | def searchSquares(self, query, continuationToken=None, limit=50): 482 | rq = SearchSquaresRequest() 483 | rq.query = query 484 | rq.continuationToken = continuationToken 485 | rq.limit = limit 486 | return self.square.searchSquares(rq) 487 | 488 | @loggedIn 489 | def refreshSubscriptions(self, subscriptions=[]): 490 | rq = RefreshSubscriptionsRequest() 491 | rq.subscriptions = subscriptions 492 | return self.square.refreshSubscriptions(rq) 493 | 494 | @loggedIn 495 | def removeSubscriptions(self, unsubscriptions=[]): 496 | rq = RemoveSubscriptionsRequest() 497 | rq.unsubscriptions = unsubscriptions 498 | return self.square.removeSubscriptions(rq) 499 | -------------------------------------------------------------------------------- /linepy/talk.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from akad.ttypes import Message, Location 3 | from random import randint 4 | 5 | import json, ntpath 6 | 7 | def loggedIn(func): 8 | def checkLogin(*args, **kwargs): 9 | if args[0].isLogin: 10 | return func(*args, **kwargs) 11 | else: 12 | args[0].callback.default('You want to call the function, you must login to LINE') 13 | return checkLogin 14 | 15 | class Talk(object): 16 | isLogin = False 17 | _messageReq = {} 18 | _unsendMessageReq = 0 19 | 20 | def __init__(self): 21 | self.isLogin = True 22 | 23 | """User""" 24 | 25 | @loggedIn 26 | def acquireEncryptedAccessToken(self, featureType=2): 27 | return self.talk.acquireEncryptedAccessToken(featureType) 28 | 29 | @loggedIn 30 | def getProfile(self): 31 | return self.talk.getProfile() 32 | 33 | @loggedIn 34 | def getSettings(self): 35 | return self.talk.getSettings() 36 | 37 | @loggedIn 38 | def getUserTicket(self): 39 | return self.talk.getUserTicket() 40 | 41 | @loggedIn 42 | def generateUserTicket(self): 43 | try: 44 | ticket = self.getUserTicket().id 45 | except: 46 | self.reissueUserTicket() 47 | ticket = self.getUserTicket().id 48 | return ticket 49 | 50 | @loggedIn 51 | def updateProfile(self, profileObject): 52 | return self.talk.updateProfile(0, profileObject) 53 | 54 | @loggedIn 55 | def updateSettings(self, settingObject): 56 | return self.talk.updateSettings(0, settingObject) 57 | 58 | @loggedIn 59 | def updateProfileAttribute(self, attrId, value): 60 | return self.talk.updateProfileAttribute(0, attrId, value) 61 | 62 | @loggedIn 63 | def updateContactSetting(self, mid, flag, value): 64 | return self.talk.updateContactSetting(0, mid, flag, value) 65 | 66 | @loggedIn 67 | def deleteContact(self, mid): 68 | return self.updateContactSetting(mid, 16, 'True') 69 | 70 | @loggedIn 71 | def renameContact(self, mid, name): 72 | return self.updateContactSetting(mid, 2, name) 73 | 74 | @loggedIn 75 | def addToFavoriteContactMids(self, mid): 76 | return self.updateContactSetting(mid, 8, 'True') 77 | 78 | @loggedIn 79 | def addToHiddenContactMids(self, mid): 80 | return self.updateContactSetting(mid, 4, 'True') 81 | 82 | """Operation""" 83 | 84 | @loggedIn 85 | def fetchOps(self, localRev, count, globalRev=0, individualRev=0): 86 | return self.poll.fetchOps(self, localRev, count, globalRev, individualRev) 87 | 88 | @loggedIn 89 | def fetchOperation(self, revision, count=1): 90 | return self.poll.fetchOperations(revision, count) 91 | 92 | @loggedIn 93 | def getLastOpRevision(self): 94 | return self.poll.getLastOpRevision() 95 | 96 | """Message""" 97 | 98 | @loggedIn 99 | def sendMessage(self, to="replyMessage", text=None, contentMetadata={}, contentType=0): 100 | if to != "replyMessage": 101 | msg = Message( 102 | to = to, 103 | text = text, 104 | contentMetadata = contentMetadata, 105 | contentType = contentType 106 | ) 107 | else: 108 | msg = Message( 109 | to = self.to, 110 | text = text, 111 | contentMetadata = contentMetadata, 112 | relatedMessageId = self.id, 113 | messageRelationType = 3, 114 | relatedMessageServiceCode = 1, 115 | contentType = contentType 116 | ) 117 | return self.talk.sendMessage(0, msg) 118 | 119 | @loggedIn 120 | def replyMessage(self, text, reply="replyMessage", contentMetadata={}): 121 | for i in range(0, len(text), 10000): 122 | txt = text[i:i+10000] 123 | if reply == "replyMessage": 124 | try: 125 | reply = self.sendMessage(self.to, txt, contentMetadata) 126 | except: 127 | reply = self.sendMessage(reply, txt, contentMetadata) 128 | else: 129 | raise Exception("Invalid Method") 130 | return reply 131 | 132 | @loggedIn 133 | def sendMessageObject(self, msg): 134 | to = msg.to 135 | if to not in self._messageReq: 136 | self._messageReq[to] = -1 137 | self._messageReq[to] += 1 138 | return self.talk.sendMessage(self._messageReq[to], msg) 139 | 140 | @loggedIn 141 | def sendLocation(self, to, address, latitude, longitude, phone=None, contentMetadata={}): 142 | msg = Message() 143 | msg.to, msg._from = to, self.profile.mid 144 | msg.text = "Location by Hello World" 145 | msg.contentType, msg.contentMetadata = 0, contentMetadata 146 | location = Location() 147 | location.address = address 148 | location.phone = phone 149 | location.latitude = float(latitude) 150 | location.longitude = float(longitude) 151 | location.title = "Location" 152 | msg.location = location 153 | if to not in self._messageReq: 154 | self._messageReq[to] = -1 155 | self._messageReq[to] += 1 156 | return self.talk.sendMessage(self._messageReq[to], msg) 157 | 158 | @loggedIn 159 | def sendMessageMusic(self, to, title=None, subText=None, url=None, iconurl=None, contentMetadata={}): 160 | """ 161 | a : Android 162 | i : Ios 163 | """ 164 | self.profile = self.getProfile() 165 | self.userTicket = self.generateUserTicket() 166 | title = title if title else 'LINE MUSIC' 167 | subText = subText if subText else self.profile.displayName 168 | url = url if url else 'line://ti/p/' + self.userTicket 169 | iconurl = iconurl if iconurl else 'https://obs.line-apps.com/os/p/%s' % self.profile.mid 170 | msg = Message() 171 | msg.to, msg._from = to, self.profile.mid 172 | msg.text = title 173 | msg.contentType = 19 174 | msg.contentMetadata = { 175 | 'text': title, 176 | 'subText': subText, 177 | 'a-installUrl': url, 178 | 'i-installUrl': url, 179 | 'a-linkUri': url, 180 | 'i-linkUri': url, 181 | 'linkUri': url, 182 | 'previewUrl': iconurl, 183 | 'type': 'mt', 184 | 'a-packageName': 'com.spotify.music', 185 | 'countryCode': 'JP', 186 | 'id': 'mt000000000a6b79f9' 187 | } 188 | if contentMetadata: 189 | msg.contentMetadata.update(contentMetadata) 190 | if to not in self._messageReq: 191 | self._messageReq[to] = -1 192 | self._messageReq[to] += 1 193 | return self.talk.sendMessage(self._messageReq[to], msg) 194 | 195 | @loggedIn 196 | def generateMessageFooter(self, title=None, link=None, iconlink=None): 197 | self.profile = self.getProfile() 198 | self.userTicket = self.generateUserTicket() 199 | title = title if title else self.profile.displayName 200 | link = link if link else 'line://ti/p/' + self.userTicket 201 | iconlink = iconlink if iconlink else 'https://obs.line-apps.com/os/p/%s' % self.profile.mid 202 | return {'AGENT_NAME': title, 'AGENT_LINK': link, 'AGENT_ICON': iconlink} 203 | 204 | @loggedIn 205 | def sendMessageWithFooter(self, to, text, title=None, link=None, iconlink=None, contentMetadata={}): 206 | msg = Message() 207 | msg.to, msg._from = to, self.profile.mid 208 | msg.text = text 209 | msg.contentType = 0 210 | msg.contentMetadata = self.generateMessageFooter(title, link, iconlink) 211 | if contentMetadata: 212 | msg.contentMetadata.update(contentMetadata) 213 | if to not in self._messageReq: 214 | self._messageReq[to] = -1 215 | self._messageReq[to] += 1 216 | return self.talk.sendMessage(self._messageReq[to], msg) 217 | 218 | @loggedIn 219 | def generateReplyMessage(self, relatedMessageId): 220 | msg = Message() 221 | msg.relatedMessageServiceCode = 1 222 | msg.messageRelationType = 3 223 | msg.relatedMessageId = str(relatedMessageId) 224 | return msg 225 | 226 | @loggedIn 227 | def sendReplyMessage(self, relatedMessageId, to, text, contentMetadata={}, contentType=0): 228 | msg = self.generateReplyMessage(relatedMessageId) 229 | msg.to = to 230 | msg.text = text 231 | msg.contentType = contentType 232 | msg.contentMetadata = contentMetadata 233 | if to not in self._messageReq: 234 | self._messageReq[to] = -1 235 | self._messageReq[to] += 1 236 | return self.talk.sendMessage(self._messageReq[to], msg) 237 | 238 | @loggedIn 239 | def sendMention(self, to, mid, firstmessage='', lastmessage=''): 240 | arrData = "" 241 | text = "%s " %(str(firstmessage)) 242 | arr = [] 243 | mention = "@zeroxyuuki " 244 | slen = str(len(text)) 245 | elen = str(len(text) + len(mention) - 1) 246 | arrData = {'S':slen, 'E':elen, 'M':mid} 247 | arr.append(arrData) 248 | text += mention + str(lastmessage) 249 | self.sendMessage(to, text, {'MENTION': str('{"MENTIONEES":' + json.dumps(arr) + '}')}, 0) 250 | 251 | @loggedIn 252 | def sendMentionV2(self, to, text="", mids=[], isUnicode=False): 253 | arrData = "" 254 | arr = [] 255 | mention = "@zeroxyuuki " 256 | if mids == []: 257 | raise Exception("Invalid mids") 258 | if "@!" in text: 259 | if text.count("@!") != len(mids): 260 | raise Exception("Invalid mids") 261 | texts = text.split("@!") 262 | textx = "" 263 | unicode = "" 264 | if isUnicode: 265 | for mid in mids: 266 | unicode += str(texts[mids.index(mid)].encode('unicode-escape')) 267 | textx += str(texts[mids.index(mid)]) 268 | slen = len(textx) if unicode == textx else len(textx) + unicode.count('U0') 269 | elen = len(textx) + 15 270 | arrData = {'S':str(slen), 'E':str(elen - 4), 'M':mid} 271 | arr.append(arrData) 272 | textx += mention 273 | else: 274 | for mid in mids: 275 | textx += str(texts[mids.index(mid)]) 276 | slen = len(textx) 277 | elen = len(textx) + 15 278 | arrData = {'S':str(slen), 'E':str(elen - 4), 'M':mid} 279 | arr.append(arrData) 280 | textx += mention 281 | textx += str(texts[len(mids)]) 282 | else: 283 | raise Exception("Invalid mention position") 284 | self.sendMessage(to, textx, {'MENTION': str('{"MENTIONEES":' + json.dumps(arr) + '}')}, 0) 285 | 286 | @loggedIn 287 | def sendMentionV3(self, *args, **kwargs): 288 | data = list(args[1]) if type(args[1]) is dict else args[1] if type(args[1]) is list else [args[1]] 289 | arrData = "" 290 | arr = [] 291 | mention = "@RhyN" 292 | if not data: 293 | raise Exception("Invalid data") 294 | if "@!" in args[0]: 295 | if args[0].count("@!") != len(data): 296 | raise Exception("Invalid count @!") 297 | _t = args[0].split("@!") 298 | _d = '' 299 | for m in range(len(data)): 300 | _d += f'{_t[m]}' 301 | slen = len(_d) 302 | elen = len(_d) + 5 303 | arrData = {'S':str(slen), 'E':str(elen), 'M':data[m]} 304 | arr.append(arrData) 305 | _d += mention 306 | _d += str(_t[len(data)])+' ' 307 | self.replyMessage(_d, contentMetadata={'MENTION': str('{"MENTIONEES":' + json.dumps(arr) + '}')}) 308 | 309 | 310 | """ Usage: 311 | @to Integer 312 | @text String 313 | @dataMid List of user Mid 314 | """ 315 | @loggedIn 316 | def sendMessageWithMention(self, to, text='', dataMid=[]): 317 | arr = [] 318 | list_text='' 319 | if '[list]' in text.lower(): 320 | i=0 321 | for l in dataMid: 322 | list_text+='\n@[list-'+str(i)+']' 323 | i=i+1 324 | text=text.replace('[list]', list_text) 325 | elif '[list-' in text.lower(): 326 | text=text 327 | else: 328 | i=0 329 | for l in dataMid: 330 | list_text+=' @[list-'+str(i)+']' 331 | i=i+1 332 | text=text+list_text 333 | i=0 334 | for l in dataMid: 335 | mid=l 336 | name='@[list-'+str(i)+']' 337 | ln_text=text.replace('\n',' ') 338 | if ln_text.find(name): 339 | line_s=int(ln_text.index(name)) 340 | line_e=(int(line_s)+int(len(name))) 341 | arrData={'S': str(line_s), 'E': str(line_e), 'M': mid} 342 | arr.append(arrData) 343 | i=i+1 344 | contentMetadata={'MENTION':str('{"MENTIONEES":' + json.dumps(arr).replace(' ','') + '}')} 345 | return self.sendMessage(to, text, contentMetadata) 346 | 347 | @loggedIn 348 | def sendSticker(self, to, packageId, stickerId): 349 | contentMetadata = { 350 | 'STKVER': '100', 351 | 'STKPKGID': packageId, 352 | 'STKID': stickerId 353 | } 354 | return self.sendMessage(to, '', contentMetadata, 7) 355 | 356 | @loggedIn 357 | def sendContact(self, to, mid): 358 | contentMetadata = {'mid': mid} 359 | return self.sendMessage(to, '', contentMetadata, 13) 360 | 361 | @loggedIn 362 | def sendGift(self, to, productId, productType): 363 | if productType not in ['theme','sticker']: 364 | raise Exception('Invalid productType value') 365 | contentMetadata = { 366 | 'MSGTPL': str(randint(0, 12)), 367 | 'PRDTYPE': productType.upper(), 368 | 'STKPKGID' if productType == 'sticker' else 'PRDID': productId 369 | } 370 | return self.sendMessage(to, '', contentMetadata, 9) 371 | 372 | @loggedIn 373 | def sendMessageAwaitCommit(self, to, text, contentMetadata={}, contentType=0): 374 | msg = Message() 375 | msg.to, msg._from = to, self.profile.mid 376 | msg.text = text 377 | msg.contentType, msg.contentMetadata = contentType, contentMetadata 378 | if to not in self._messageReq: 379 | self._messageReq[to] = -1 380 | self._messageReq[to] += 1 381 | return self.talk.sendMessageAwaitCommit(self._messageReq[to], msg) 382 | 383 | @loggedIn 384 | def unsendMessage(self, messageId): 385 | self._unsendMessageReq += 1 386 | return self.talk.unsendMessage(self._unsendMessageReq, messageId) 387 | 388 | @loggedIn 389 | def requestResendMessage(self, senderMid, messageId): 390 | return self.talk.requestResendMessage(0, senderMid, messageId) 391 | 392 | @loggedIn 393 | def respondResendMessage(self, receiverMid, originalMessageId, resendMessage, errorCode): 394 | return self.talk.respondResendMessage(0, receiverMid, originalMessageId, resendMessage, errorCode) 395 | 396 | @loggedIn 397 | def removeMessage(self, messageId): 398 | return self.talk.removeMessage(messageId) 399 | 400 | @loggedIn 401 | def removeAllMessages(self, lastMessageId): 402 | return self.talk.removeAllMessages(0, lastMessageId) 403 | 404 | @loggedIn 405 | def removeMessageFromMyHome(self, messageId): 406 | return self.talk.removeMessageFromMyHome(messageId) 407 | 408 | @loggedIn 409 | def destroyMessage(self, chatId, messageId): 410 | return self.talk.destroyMessage(0, chatId, messageId, sessionId) 411 | 412 | @loggedIn 413 | def sendChatChecked(self, consumer, messageId): 414 | return self.talk.sendChatChecked(0, consumer, messageId) 415 | 416 | @loggedIn 417 | def sendEvent(self, messageObject): 418 | return self.talk.sendEvent(0, messageObject) 419 | 420 | @loggedIn 421 | def getLastReadMessageIds(self, chatId): 422 | return self.talk.getLastReadMessageIds(0, chatId) 423 | 424 | @loggedIn 425 | def getPreviousMessagesV2WithReadCount(self, messageBoxId, endMessageId, messagesCount=50): 426 | return self.talk.getPreviousMessagesV2WithReadCount(messageBoxId, endMessageId, messagesCount) 427 | 428 | """Object""" 429 | 430 | @loggedIn 431 | def sendImage(self, to, path): 432 | objectId = self.sendMessage(to=to, text=None, contentType = 1).id 433 | return self.uploadObjTalk(path=path, type='image', returnAs='bool', objId=objectId) 434 | 435 | @loggedIn 436 | def sendImageWithURL(self, to, url): 437 | path = self.downloadFileURL(url, 'path') 438 | return self.sendImage(to, path) 439 | 440 | @loggedIn 441 | def sendGIF(self, to, path): 442 | return self.uploadObjTalk(path=path, type='gif', returnAs='bool', to=to) 443 | 444 | @loggedIn 445 | def sendGIFWithURL(self, to, url): 446 | path = self.downloadFileURL(url, 'path') 447 | return self.sendGIF(to, path) 448 | 449 | @loggedIn 450 | def sendVideo(self, to, path): 451 | objectId = self.sendMessage(to=to, text=None, contentMetadata={'VIDLEN': '60000','DURATION': '60000'}, contentType = 2).id 452 | return self.uploadObjTalk(path=path, type='video', returnAs='bool', objId=objectId) 453 | 454 | @loggedIn 455 | def sendVideoWithURL(self, to, url): 456 | path = self.downloadFileURL(url, 'path') 457 | return self.sendVideo(to, path) 458 | 459 | @loggedIn 460 | def sendAudio(self, to, path): 461 | objectId = self.sendMessage(to=to, text=None, contentType = 3).id 462 | return self.uploadObjTalk(path=path, type='audio', returnAs='bool', objId=objectId) 463 | 464 | @loggedIn 465 | def sendAudioWithURL(self, to, url): 466 | path = self.downloadFileURL(url, 'path') 467 | return self.sendAudio(to, path) 468 | 469 | @loggedIn 470 | def sendFile(self, to, path, file_name=''): 471 | if file_name == '': 472 | file_name = ntpath.basename(path) 473 | file_size = len(open(path, 'rb').read()) 474 | objectId = self.sendMessage(to=to, text=None, contentMetadata={'FILE_NAME': str(file_name),'FILE_SIZE': str(file_size)}, contentType = 14).id 475 | return self.uploadObjTalk(path=path, type='file', returnAs='bool', objId=objectId, name=file_name) 476 | 477 | @loggedIn 478 | def sendFileWithURL(self, to, url, fileName=''): 479 | path = self.downloadFileURL(url, 'path') 480 | return self.sendFile(to, path, fileName) 481 | 482 | """Contact""" 483 | 484 | @loggedIn 485 | def blockContact(self, mid): 486 | return self.talk.blockContact(0, mid) 487 | 488 | @loggedIn 489 | def unblockContact(self, mid): 490 | return self.talk.unblockContact(0, mid) 491 | 492 | @loggedIn 493 | def findAndAddContactByMetaTag(self, userid, reference): 494 | return self.talk.findAndAddContactByMetaTag(0, userid, reference) 495 | 496 | @loggedIn 497 | def findAndAddContactsByMid(self, mid): 498 | return self.talk.findAndAddContactsByMid(0, mid, 0, '') 499 | 500 | @loggedIn 501 | def findAndAddContactsByEmail(self, emails=[]): 502 | return self.talk.findAndAddContactsByEmail(0, emails) 503 | 504 | @loggedIn 505 | def findAndAddContactsByUserid(self, userid): 506 | return self.talk.findAndAddContactsByUserid(0, userid) 507 | 508 | @loggedIn 509 | def findContactsByUserid(self, userid): 510 | return self.talk.findContactByUserid(userid) 511 | 512 | @loggedIn 513 | def findContactByTicket(self, ticketId): 514 | return self.talk.findContactByUserTicket(ticketId) 515 | 516 | @loggedIn 517 | def getAllContactIds(self): 518 | return self.talk.getAllContactIds() 519 | 520 | @loggedIn 521 | def getBlockedContactIds(self): 522 | return self.talk.getBlockedContactIds() 523 | 524 | @loggedIn 525 | def getContact(self, mid): 526 | return self.talk.getContact(mid) 527 | 528 | @loggedIn 529 | def getContacts(self, midlist): 530 | return self.talk.getContacts(midlist) 531 | 532 | @loggedIn 533 | def getFavoriteMids(self): 534 | return self.talk.getFavoriteMids() 535 | 536 | @loggedIn 537 | def getHiddenContactMids(self): 538 | return self.talk.getHiddenContactMids() 539 | 540 | @loggedIn 541 | def tryFriendRequest(self, midOrEMid, friendRequestParams, method=1): 542 | return self.talk.tryFriendRequest(midOrEMid, method, friendRequestParams) 543 | 544 | @loggedIn 545 | def makeUserAddMyselfAsContact(self, contactOwnerMid): 546 | return self.talk.makeUserAddMyselfAsContact(contactOwnerMid) 547 | 548 | @loggedIn 549 | def getContactWithFriendRequestStatus(self, id): 550 | return self.talk.getContactWithFriendRequestStatus(id) 551 | 552 | @loggedIn 553 | def reissueUserTicket(self, expirationTime=100, maxUseCount=100): 554 | return self.talk.reissueUserTicket(expirationTime, maxUseCount) 555 | 556 | @loggedIn 557 | def cloneContactProfile(self, mid, channel): 558 | contact = self.getContact(mid) 559 | path = "http://dl.profile.line-cdn.net/" + contact.pictureStatus 560 | path = self.downloadFileURL(path) 561 | self.updateProfilePicture(path) 562 | profile = self.profile 563 | profile.displayName = contact.displayName 564 | profile.statusMessage = contact.statusMessage 565 | if channel.getProfileCoverId(mid) is not None: 566 | channel.updateProfileCoverById(channel.getProfileCoverId(mid)) 567 | return self.updateProfile(profile) 568 | 569 | """Group""" 570 | 571 | @loggedIn 572 | def getChatRoomAnnouncementsBulk(self, chatRoomMids): 573 | return self.talk.getChatRoomAnnouncementsBulk(chatRoomMids) 574 | 575 | @loggedIn 576 | def getChatRoomAnnouncements(self, chatRoomMid): 577 | return self.talk.getChatRoomAnnouncements(chatRoomMid) 578 | 579 | @loggedIn 580 | def createChatRoomAnnouncement(self, chatRoomMid, type, contents): 581 | return self.talk.createChatRoomAnnouncement(0, chatRoomMid, type, contents) 582 | 583 | @loggedIn 584 | def removeChatRoomAnnouncement(self, chatRoomMid, announcementSeq): 585 | return self.talk.removeChatRoomAnnouncement(0, chatRoomMid, announcementSeq) 586 | 587 | @loggedIn 588 | def getGroupWithoutMembers(self, groupId): 589 | return self.talk.getGroupWithoutMembers(groupId) 590 | 591 | @loggedIn 592 | def findGroupByTicket(self, ticketId): 593 | return self.talk.findGroupByTicket(ticketId) 594 | 595 | @loggedIn 596 | def acceptGroupInvitation(self, groupId): 597 | return self.talk.acceptGroupInvitation(0, groupId) 598 | 599 | @loggedIn 600 | def acceptGroupInvitationByTicket(self, groupId, ticketId): 601 | return self.talk.acceptGroupInvitationByTicket(0, groupId, ticketId) 602 | 603 | @loggedIn 604 | def cancelGroupInvitation(self, groupId, contactIds): 605 | return self.talk.cancelGroupInvitation(0, groupId, contactIds) 606 | 607 | @loggedIn 608 | def createGroup(self, name, midlist): 609 | return self.talk.createGroup(0, name, midlist) 610 | 611 | @loggedIn 612 | def getGroup(self, groupId): 613 | return self.talk.getGroup(groupId) 614 | 615 | @loggedIn 616 | def getGroups(self, groupIds): 617 | return self.talk.getGroups(groupIds) 618 | 619 | @loggedIn 620 | def getGroupsV2(self, groupIds): 621 | return self.talk.getGroupsV2(groupIds) 622 | 623 | @loggedIn 624 | def getCompactGroup(self, groupId): 625 | return self.talk.getCompactGroup(groupId) 626 | 627 | @loggedIn 628 | def getCompactRoom(self, roomId): 629 | return self.talk.getCompactRoom(roomId) 630 | 631 | @loggedIn 632 | def getGroupIdsByName(self, groupName): 633 | gIds = [] 634 | for gId in self.getGroupIdsJoined(): 635 | g = self.getCompactGroup(gId) 636 | if groupName in g.name: 637 | gIds.append(gId) 638 | return gIds 639 | 640 | @loggedIn 641 | def getGroupIdsInvited(self): 642 | return self.talk.getGroupIdsInvited() 643 | 644 | @loggedIn 645 | def getGroupIdsJoined(self): 646 | return self.talk.getGroupIdsJoined() 647 | 648 | @loggedIn 649 | def updateGroupPreferenceAttribute(self, groupMid, updatedAttrs): 650 | return self.talk.updateGroupPreferenceAttribute(0, groupMid, updatedAttrs) 651 | 652 | @loggedIn 653 | def inviteIntoGroup(self, groupId, midlist): 654 | return self.talk.inviteIntoGroup(0, groupId, midlist) 655 | 656 | @loggedIn 657 | def kickoutFromGroup(self, groupId, midlist): 658 | return self.talk.kickoutFromGroup(0, groupId, midlist) 659 | 660 | @loggedIn 661 | def leaveGroup(self, groupId): 662 | return self.talk.leaveGroup(0, groupId) 663 | 664 | @loggedIn 665 | def rejectGroupInvitation(self, groupId): 666 | return self.talk.rejectGroupInvitation(0, groupId) 667 | 668 | @loggedIn 669 | def reissueGroupTicket(self, groupId): 670 | return self.talk.reissueGroupTicket(groupId) 671 | 672 | @loggedIn 673 | def updateGroup(self, groupObject): 674 | return self.talk.updateGroup(0, groupObject) 675 | 676 | """Room""" 677 | 678 | @loggedIn 679 | def createRoom(self, midlist): 680 | return self.talk.createRoom(0, midlist) 681 | 682 | @loggedIn 683 | def getRoom(self, roomId): 684 | return self.talk.getRoom(roomId) 685 | 686 | @loggedIn 687 | def inviteIntoRoom(self, roomId, midlist): 688 | return self.talk.inviteIntoRoom(0, roomId, midlist) 689 | 690 | @loggedIn 691 | def leaveRoom(self, roomId): 692 | return self.talk.leaveRoom(0, roomId) 693 | 694 | """Call""" 695 | 696 | @loggedIn 697 | def acquireCallTalkRoute(self, to): 698 | return self.talk.acquireCallRoute(to) 699 | 700 | """Report""" 701 | 702 | @loggedIn 703 | def reportSpam(self, chatMid, memberMids=[], spammerReasons=[], senderMids=[], spamMessageIds=[], spamMessages=[]): 704 | return self.talk.reportSpam(chatMid, memberMids, spammerReasons, senderMids, spamMessageIds, spamMessages) 705 | 706 | @loggedIn 707 | def reportSpammer(self, spammerMid, spammerReasons=[], spamMessageIds=[]): 708 | return self.talk.reportSpammer(spammerMid, spammerReasons, spamMessageIds) 709 | -------------------------------------------------------------------------------- /linepy/timeline.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from datetime import datetime 3 | from .channel import Channel 4 | 5 | import json, time, base64 6 | 7 | def loggedIn(func): 8 | def checkLogin(*args, **kwargs): 9 | if args[0].isLogin: 10 | return func(*args, **kwargs) 11 | else: 12 | args[0].callback.default('You want to call the function, you must login to LINE') 13 | return checkLogin 14 | 15 | class Timeline(Channel): 16 | 17 | def __init__(self): 18 | if not self.channelId: 19 | self.channelId = self.server.CHANNEL_ID['LINE_TIMELINE'] 20 | Channel.__init__(self, self.channel, self.channelId, False) 21 | self.tl = self.getChannelResult() 22 | self.__loginTimeline() 23 | 24 | def __loginTimeline(self): 25 | self.server.setTimelineHeadersWithDict({ 26 | 'Content-Type': 'application/json', 27 | 'User-Agent': self.server.USER_AGENT, 28 | 'X-Line-Mid': self.profile.mid, 29 | 'X-Line-Carrier': self.server.CARRIER, 30 | 'X-Line-Application': self.server.APP_NAME, 31 | 'X-Line-ChannelToken': self.tl.channelAccessToken 32 | }) 33 | self.profileDetail = self.getProfileDetail() 34 | 35 | """Timeline""" 36 | 37 | @loggedIn 38 | def getFeed(self, postLimit=10, commentLimit=1, likeLimit=1, order='TIME'): 39 | params = {'postLimit': postLimit, 'commentLimit': commentLimit, 'likeLimit': likeLimit, 'order': order} 40 | url = self.server.urlEncode(self.server.LINE_TIMELINE_API, '/v45/feed/list.json', params) 41 | r = self.server.getContent(url, headers=self.server.timelineHeaders) 42 | return r.json() 43 | 44 | @loggedIn 45 | def getHomeProfile(self, mid=None, postLimit=10, commentLimit=1, likeLimit=1): 46 | if mid is None: 47 | mid = self.profile.mid 48 | params = {'homeId': mid, 'postLimit': postLimit, 'commentLimit': commentLimit, 'likeLimit': likeLimit, 'sourceType': 'LINE_PROFILE_COVER'} 49 | url = self.server.urlEncode(self.server.LINE_TIMELINE_API, '/v45/post/list.json', params) 50 | r = self.server.getContent(url, headers=self.server.timelineHeaders) 51 | return r.json() 52 | 53 | @loggedIn 54 | def getProfileDetail(self, mid=None): 55 | if mid is None: 56 | mid = self.profile.mid 57 | params = {'userMid': mid} 58 | url = self.server.urlEncode(self.server.LINE_TIMELINE_API, '/v1/userpopup/getDetail.json', params) 59 | r = self.server.getContent(url, headers=self.server.timelineHeaders) 60 | return r.json() 61 | 62 | @loggedIn 63 | def updateProfileCoverById(self, objId): 64 | params = {'coverImageId': objId} 65 | url = self.server.urlEncode(self.server.LINE_TIMELINE_API, '/v45/home/updateCover.json', params) 66 | r = self.server.getContent(url, headers=self.server.timelineHeaders) 67 | return r.json() 68 | 69 | @loggedIn 70 | def getProfileCoverId(self, mid=None): 71 | if mid is None: 72 | mid = self.profile.mid 73 | home = self.getProfileDetail(mid) 74 | return home['result']['objectId'] 75 | 76 | @loggedIn 77 | def getProfileCoverURL(self, mid=None): 78 | if mid is None: 79 | mid = self.profile.mid 80 | home = self.getProfileDetail(mid) 81 | params = {'userid': mid, 'oid': home['result']['objectId']} 82 | return self.server.urlEncode(self.server.LINE_OBS_DOMAIN, '/myhome/c/download.nhn', params) 83 | 84 | """Post""" 85 | 86 | @loggedIn 87 | def createPost(self, text, holdingTime=None): 88 | params = {'homeId': self.profile.mid, 'sourceType': 'TIMELINE'} 89 | url = self.server.urlEncode(self.server.LINE_TIMELINE_API, '/v45/post/create.json', params) 90 | payload = {'postInfo': {'readPermission': {'type': 'ALL'}}, 'sourceType': 'TIMELINE', 'contents': {'text': text}} 91 | if holdingTime != None: 92 | payload["postInfo"]["holdingTime"] = holdingTime 93 | data = json.dumps(payload) 94 | r = self.server.postContent(url, data=data, headers=self.server.timelineHeaders) 95 | return r.json() 96 | 97 | @loggedIn 98 | def sendPostToTalk(self, mid, postId): 99 | if mid is None: 100 | mid = self.profile.mid 101 | params = {'receiveMid': mid, 'postId': postId} 102 | url = self.server.urlEncode(self.server.LINE_TIMELINE_API, '/v45/post/sendPostToTalk.json', params) 103 | r = self.server.getContent(url, headers=self.server.timelineHeaders) 104 | return r.json() 105 | 106 | @loggedIn 107 | def createComment(self, mid, postId, text): 108 | if mid is None: 109 | mid = self.profile.mid 110 | params = {'homeId': mid, 'sourceType': 'TIMELINE'} 111 | url = self.server.urlEncode(self.server.LINE_TIMELINE_API, '/v45/comment/create.json', params) 112 | data = {'commentText': text, 'activityExternalId': postId, 'actorId': mid} 113 | data = json.dumps(data) 114 | r = self.server.postContent(url, data=data, headers=self.server.timelineHeaders) 115 | return r.json() 116 | 117 | @loggedIn 118 | def deleteComment(self, mid, postId, commentId): 119 | if mid is None: 120 | mid = self.profile.mid 121 | params = {'homeId': mid, 'sourceType': 'TIMELINE'} 122 | url = self.server.urlEncode(self.server.LINE_TIMELINE_API, '/v45/comment/delete.json', params) 123 | data = {'commentId': commentId, 'activityExternalId': postId, 'actorId': mid} 124 | data = json.dumps(data) 125 | r = self.server.postContent(url, data=data, headers=self.server.timelineHeaders) 126 | return r.json() 127 | 128 | @loggedIn 129 | def likePost(self, mid, postId, likeType=1001): 130 | if mid is None: 131 | mid = self.profile.mid 132 | if likeType not in [1001,1002,1003,1004,1005,1006]: 133 | raise Exception('Invalid parameter likeType') 134 | params = {'homeId': mid, 'sourceType': 'TIMELINE'} 135 | url = self.server.urlEncode(self.server.LINE_TIMELINE_API, '/v45/like/create.json', params) 136 | data = {'likeType': likeType, 'activityExternalId': postId, 'actorId': mid} 137 | data = json.dumps(data) 138 | r = self.server.postContent(url, data=data, headers=self.server.timelineHeaders) 139 | return r.json() 140 | 141 | @loggedIn 142 | def unlikePost(self, mid, postId): 143 | if mid is None: 144 | mid = self.profile.mid 145 | params = {'homeId': mid, 'sourceType': 'TIMELINE'} 146 | url = self.server.urlEncode(self.server.LINE_TIMELINE_API, '/v45/like/cancel.json', params) 147 | data = {'activityExternalId': postId, 'actorId': mid} 148 | data = json.dumps(data) 149 | r = self.server.postContent(url, data=data, headers=self.server.timelineHeaders) 150 | return r.json() 151 | 152 | """Group Post""" 153 | 154 | @loggedIn 155 | def createGroupPost(self, mid, text): 156 | payload = {'postInfo': {'readPermission': {'homeId': mid}}, 'sourceType': 'TIMELINE', 'contents': {'text': text}} 157 | data = json.dumps(payload) 158 | r = self.server.postContent(self.server.LINE_TIMELINE_API + '/v45/post/create.json', data=data, headers=self.server.timelineHeaders) 159 | return r.json() 160 | 161 | @loggedIn 162 | def createGroupAlbum(self, mid, name): 163 | data = json.dumps({'title': name, 'type': 'image'}) 164 | params = {'homeId': mid,'count': '1','auto': '0'} 165 | url = self.server.urlEncode(self.server.LINE_TIMELINE_MH, '/album/v3/album.json', params) 166 | r = self.server.postContent(url, data=data, headers=self.server.timelineHeaders) 167 | if r.status_code != 201: 168 | raise Exception('Create a new album failure.') 169 | return True 170 | 171 | @loggedIn 172 | def deleteGroupAlbum(self, mid, albumId): 173 | params = {'homeId': mid} 174 | url = self.server.urlEncode(self.server.LINE_TIMELINE_MH, '/album/v3/album/%s' % albumId, params) 175 | r = self.server.deleteContent(url, headers=self.server.timelineHeaders) 176 | if r.status_code != 201: 177 | raise Exception('Delete album failure.') 178 | return True 179 | 180 | @loggedIn 181 | def getGroupPost(self, mid, postLimit=10, commentLimit=1, likeLimit=1): 182 | params = {'homeId': mid, 'commentLimit': commentLimit, 'likeLimit': likeLimit, 'sourceType': 'TALKROOM'} 183 | url = self.server.urlEncode(self.server.LINE_TIMELINE_API, '/v45/post/list.json', params) 184 | r = self.server.getContent(url, headers=self.server.timelineHeaders) 185 | return r.json() 186 | 187 | """Group Album""" 188 | 189 | @loggedIn 190 | def getGroupAlbum(self, mid): 191 | params = {'homeId': mid, 'type': 'g', 'sourceType': 'TALKROOM'} 192 | url = self.server.urlEncode(self.server.LINE_TIMELINE_MH, '/album/v3/albums.json', params) 193 | r = self.server.getContent(url, headers=self.server.timelineHeaders) 194 | return r.json() 195 | 196 | @loggedIn 197 | def changeGroupAlbumName(self, mid, albumId, name): 198 | data = json.dumps({'title': name}) 199 | params = {'homeId': mid} 200 | url = self.server.urlEncode(self.server.LINE_TIMELINE_MH, '/album/v3/album/%s' % albumId, params) 201 | r = self.server.putContent(url, data=data, headers=self.server.timelineHeaders) 202 | if r.status_code != 201: 203 | raise Exception('Change album name failure.') 204 | return True 205 | 206 | @loggedIn 207 | def addImageToAlbum(self, mid, albumId, path): 208 | file = open(path, 'rb').read() 209 | params = { 210 | 'oid': int(time.time()), 211 | 'quality': '90', 212 | 'range': len(file), 213 | 'type': 'image' 214 | } 215 | hr = self.server.additionalHeaders(self.server.timelineHeaders, { 216 | 'Content-Type': 'image/jpeg', 217 | 'X-Line-Mid': mid, 218 | 'X-Line-Album': albumId, 219 | 'x-obs-params': self.genOBSParams(params,'b64') 220 | }) 221 | r = self.server.getContent(self.server.LINE_OBS_DOMAIN + '/album/a/upload.nhn', data=file, headers=hr) 222 | if r.status_code != 201: 223 | raise Exception('Add image to album failure.') 224 | return r.json() 225 | 226 | @loggedIn 227 | def getImageGroupAlbum(self, mid, albumId, objId, returnAs='path', saveAs=''): 228 | if saveAs == '': 229 | saveAs = self.genTempFile('path') 230 | if returnAs not in ['path','bool','bin']: 231 | raise Exception('Invalid returnAs value') 232 | hr = self.server.additionalHeaders(self.server.timelineHeaders, { 233 | 'Content-Type': 'image/jpeg', 234 | 'X-Line-Mid': mid, 235 | 'X-Line-Album': albumId 236 | }) 237 | params = {'ver': '1.0', 'oid': objId} 238 | url = self.server.urlEncode(self.server.LINE_OBS_DOMAIN, '/album/a/download.nhn', params) 239 | r = self.server.getContent(url, headers=hr) 240 | if r.status_code == 200: 241 | self.saveFile(saveAs, r.raw) 242 | if returnAs == 'path': 243 | return saveAs 244 | elif returnAs == 'bool': 245 | return True 246 | elif returnAs == 'bin': 247 | return r.raw 248 | else: 249 | raise Exception('Download image album failure.') 250 | -------------------------------------------------------------------------------- /linepy/transport.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from io import BytesIO 3 | from six.moves import urllib, http_client 4 | import os, socket, sys, warnings, base64, time, json, asyncio, six 5 | import hyper, httpx, httplib2 6 | 7 | from thrift.transport.TTransport import TTransportBase 8 | try: 9 | from thrift.protocol import fastbinary 10 | except ImportError: 11 | fastbinary = None 12 | 13 | class THttpClient(TTransportBase): 14 | '''Http implementation of TTransport base.''' 15 | 16 | def __init__(self, uri_or_host, port=None, path=None, customThrift=False, request='httplib', http2=False, proxy_host=None, proxy_port=None, proxy_auth=None): 17 | '''THttpClient supports two different types constructor parameters. 18 | 19 | THttpClient(host, port, path) - deprecated 20 | THttpClient(uri) 21 | 22 | Only the second supports https. 23 | ''' 24 | if port is not None: 25 | warnings.warn( 26 | 'Please use the THttpClient("http://host:port/path") syntax', 27 | DeprecationWarning, 28 | stacklevel=2 29 | ) 30 | self.host = uri_or_host 31 | self.port = port 32 | assert path 33 | self.path = path 34 | self.scheme = 'http' 35 | else: 36 | parsed = urllib.parse.urlparse(uri_or_host) 37 | self.scheme = parsed.scheme 38 | assert self.scheme in ('http', 'https') 39 | if self.scheme == 'http': 40 | self.port = parsed.port or http_client.HTTP_PORT 41 | elif self.scheme == 'https': 42 | self.port = parsed.port or http_client.HTTPS_PORT 43 | self.host = parsed.hostname 44 | self.path = parsed.path 45 | if parsed.query: 46 | self.path += '?%s' % parsed.query 47 | proxy = None 48 | self.request = request 49 | self.http2 = http2 50 | self.realhost = proxy_host 51 | self.realport = proxy_port 52 | self.proxy_auth = proxy_auth 53 | self.__wbuf = BytesIO() 54 | if self.scheme == 'https' and self.using_proxy() and self.proxy_auth: 55 | self.proxy_headers = {'Proxy-Authorization': self.proxy_auth} 56 | else: 57 | self.proxy_headers = None 58 | self.url = '%s://%s:%s%s' % (self.scheme, self.host, self.port, self.path) 59 | if customThrift: 60 | if self.request == 'hyper': 61 | if self.http2: 62 | self.__http = hyper.HTTP20Connection(self.host, self.port, proxy_host=self.realhost, proxy_port=self.realport, proxy_headers=self.proxy_headers) 63 | else: 64 | self.__http = hyper.HTTPConnection(self.host, self.port, proxy_host=self.realhost, proxy_port=self.realport, proxy_headers=self.proxy_headers) 65 | elif self.request == 'httpx': 66 | self.__http = httpx.AsyncClient(base_url='%s://%s' % (self.scheme, self.host), http2=self.http2) 67 | else: 68 | if self.http2: 69 | self.__http = httplib2.Http() 70 | elif self.scheme == 'http': 71 | self.__http = http_client.HTTPConnection(self.host, self.port) 72 | elif self.scheme == 'https': 73 | self.__http = http_client.HTTPSConnection(self.host, self.port) 74 | if self.using_proxy(): 75 | self.__http.set_tunnel(self.realhost, self.realport, self.proxy_headers) 76 | else: 77 | self.__http = None 78 | self.__async_loop = asyncio.get_event_loop() if self.request == 'httpx' else None 79 | self.__http_response = None 80 | self.__response_data = None 81 | self.__last_read = 0 82 | self.__timeout = None 83 | self.__custom_headers = None 84 | self.__time = time.time() 85 | self.__custom_thrift = customThrift 86 | self.__loop = 0 87 | 88 | @staticmethod 89 | def basic_proxy_auth_header(proxy): 90 | if proxy is None or not proxy.username: 91 | return None 92 | ap = '%s:%s' % (urllib.parse.unquote(proxy.username), 93 | urllib.parse.unquote(proxy.password)) 94 | cr = base64.b64encode(ap).strip() 95 | return 'Basic ' + cr 96 | 97 | def using_proxy(self): 98 | return self.realhost is not None 99 | 100 | def open(self): 101 | if self.request == 'hyper': 102 | if self.http2: 103 | self.__http = hyper.HTTP20Connection(self.host, self.port, proxy_host=self.realhost, proxy_port=self.realport, proxy_headers=self.proxy_headers) 104 | else: 105 | self.__http = hyper.HTTPConnection(self.host, self.port, proxy_host=self.realhost, proxy_port=self.realport, proxy_headers=self.proxy_headers) 106 | elif self.request == 'httpx': 107 | self.__http = httpx.AsyncClient(base_url='%s://%s' % (self.scheme, self.host), http2=self.http2) 108 | else: 109 | if self.http2: 110 | self.__http = httplib2.Http() 111 | elif self.scheme == 'http': 112 | self.__http = http_client.HTTPConnection(self.host, self.port) 113 | elif self.scheme == 'https': 114 | self.__http = http_client.HTTPSConnection(self.host, self.port) 115 | if self.using_proxy(): 116 | self.__http.set_tunnel(self.realhost, self.realport, self.proxy_headers) 117 | 118 | def close(self): 119 | if self.request != 'httpx': 120 | self.__http.close() 121 | self.__http = None 122 | self.__http_response = None 123 | self.__response_data = None 124 | self.__last_read = 0 125 | 126 | def getHeaders(self): 127 | return self.headers 128 | 129 | def isOpen(self): 130 | return self.__http is not None 131 | 132 | def setTimeout(self, ms): 133 | if not hasattr(socket, 'getdefaulttimeout'): 134 | raise NotImplementedError 135 | 136 | if ms is None: 137 | self.__timeout = None 138 | else: 139 | self.__timeout = ms / 1000.0 140 | 141 | def setCustomHeaders(self, headers): 142 | self.__custom_headers = headers 143 | 144 | def read(self, sz): 145 | if self.request == 'httpx' or (self.request == 'httplib' and self.http2): 146 | max_sz = self.__last_read + sz 147 | min_sz = self.__last_read 148 | self.__last_read = max_sz 149 | content = self.__response_data[min_sz:max_sz] 150 | else: 151 | content = self.__http_response.read(sz) 152 | return content 153 | 154 | def write(self, buf): 155 | self.__wbuf.write(buf) 156 | 157 | def __withTimeout(f): 158 | def _f(*args, **kwargs): 159 | orig_timeout = socket.getdefaulttimeout() 160 | socket.setdefaulttimeout(args[0].__timeout) 161 | try: 162 | result = f(*args, **kwargs) 163 | finally: 164 | socket.setdefaulttimeout(orig_timeout) 165 | return result 166 | return _f 167 | 168 | async def httpx_flush(self, data, headers): 169 | # Building httpx request 170 | request = self.__http.build_request('POST', self.path, data=data, headers=headers) 171 | 172 | # Sending httpx request 173 | self.__http_response = await self.__http.send(request) 174 | self.code = self.__http_response.status_code 175 | self.message = self.__http_response.reason_phrase 176 | self.headers = self.__http_response.headers 177 | self.__response_data = self.__http_response.read() 178 | self.__last_read = 0 179 | 180 | def flush(self): 181 | if self.__custom_thrift: 182 | if self.__loop <= 2: 183 | if self.isOpen(): self.close() 184 | self.open(); self.__loop += 1 185 | elif time.time() - self.__time > 90: 186 | self.close(); self.open(); self.__time = time.time() 187 | else: 188 | if self.isOpen(): 189 | self.close() 190 | self.open() 191 | 192 | # Pull data out of buffer 193 | data = self.__wbuf.getvalue() 194 | self.__wbuf = BytesIO() 195 | 196 | if not self.__custom_headers or 'User-Agent' not in self.__custom_headers: 197 | user_agent = 'Python/THttpClient' 198 | script = os.path.basename(sys.argv[0]) 199 | if script: 200 | user_agent = '%s (%s)' % (user_agent, urllib.parse.quote(script)) 201 | else: 202 | user_agent = None 203 | if self.request == 'hyper': 204 | headers = {'Content-Type': 'application/x-thrift', 'Content-Length': str(len(data)), 'User-Agent': user_agent} 205 | if self.__custom_headers: 206 | headers.update(self.__custom_headers) 207 | 208 | # Sending request with payload 209 | request = self.__http.request('POST', self.path, data, headers) 210 | 211 | # Get reply to flush the request 212 | self.__http_response = self.__http.get_response(request) 213 | self.code = self.__http_response.status 214 | self.message = self.__http_response.reason 215 | self.headers = self.__http_response.headers 216 | elif self.request == 'httpx': 217 | headers = {'Content-Type': 'application/x-thrift', 'Content-Length': str(len(data)), 'User-Agent': user_agent} 218 | if self.__custom_headers: 219 | headers.update(self.__custom_headers) 220 | self.__async_loop.run_until_complete(self.httpx_flush(data, headers)) 221 | elif self.request == 'httplib' and self.http2: 222 | headers = {'Content-Type': 'application/x-thrift', 'Content-Length': str(len(data)), 'User-Agent': user_agent} 223 | if self.__custom_headers: 224 | headers.update(self.__custom_headers) 225 | 226 | # Sending and get reply to request 227 | self.__http_response, self.__response_data = self.__http.request(self.url, 'POST', headers=headers, body=data) 228 | self.__last_read = 0 229 | self.code = self.__http_response.status 230 | self.message = self.__http_response.reason 231 | self.headers = self.__http_response 232 | else: 233 | # HTTP request 234 | if self.using_proxy() and self.scheme == 'http': 235 | # need full URL of real host for HTTP proxy here (HTTPS uses CONNECT tunnel) 236 | self.__http.putrequest('POST', 'http://%s:%s%s' % 237 | (self.realhost, self.realport, self.path)) 238 | else: 239 | self.__http.putrequest('POST', self.path) 240 | 241 | # Write headers 242 | self.__http.putheader('Content-Type', 'application/x-thrift') 243 | self.__http.putheader('Content-Length', str(len(data))) 244 | if not self.__custom_headers or 'User-Agent' not in self.__custom_headers: 245 | self.__http.putheader('User-Agent', user_agent) 246 | 247 | if self.__custom_headers: 248 | for key, val in six.iteritems(self.__custom_headers): 249 | self.__http.putheader(key, val) 250 | 251 | self.__http.endheaders() 252 | 253 | # Write payload 254 | self.__http.send(data) 255 | 256 | # Get reply to flush the request 257 | self.__http_response = self.__http.getresponse() 258 | self.code = self.__http_response.status 259 | self.message = self.__http_response.reason 260 | self.headers = self.__http_response.msg 261 | 262 | # Decorate if we know how to timeout 263 | if hasattr(socket, 'getdefaulttimeout'): 264 | flush = __withTimeout(flush) 265 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests 2 | rsa 3 | hyper 4 | httpx 5 | httplib2 6 | python-axolotl-curve25519 7 | pycryptodome 8 | --------------------------------------------------------------------------------