diff --git a/README.md b/README.md index 9abf317..ff786fc 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ Star Yuuki(pYthon) BOT - Yuuki ================== -![Version](https://img.shields.io/badge/v6.3.2-OpenSource-33FF33.svg) ![License](https://img.shields.io/badge/license-MPL--2.0-FF8800.svg) ![Python](https://img.shields.io/badge/python-2.x-0066FF.svg) +![Version](https://img.shields.io/badge/v6.5.0-OpenSource-33FF33.svg) ![License](https://img.shields.io/badge/license-MPL--2.0-FF8800.svg) ![Python](https://img.shields.io/badge/python-3.x-0066FF.svg) -A LINE Unofficial API Python BOT by SA Kingdom and Star Inc. +A Python BOT for LINE Group Protection with LINE Unofficial API. ![ICON](logo.png) ## Maybe you had seen it ! @@ -13,26 +13,42 @@ Now, it is OpenSource. This software using Mozilla Public License 2.0. ## Warning -The project was stopped in 2017/06. So the latest stable version is "v6.3.2". Some function of old version maybe will make your LINE account be banned. +The latest stable version is "v6.5.0". + +Some function of old version maybe will make your LINE account be banned. ## Requires - python >= 2.6 (Python 3 Not Support) - thrift >= 0.11.0 +[!] Run with GNU/Linux only -### Before v6.5.0: -It was used the edited version of [carpedm20/LINE](https://github.com/carpedm20/LINE) API to run the Application. So you need to make the LINEAPI for it. +Recommand Environment: -### After (Future): -You need the "LINE Unofficial API Python Core" only. (Ex:[olsb](https://github.com/star-inc/olsb_cores), [akad](https://pypi.org/project/akad), [curve](https://pypi.org/project/curve) ...) + Ubuntu >= 18.04 + python >= 3.7 + pip >= 19.2 -## Old Version -Maybe you want the history version of SYB. Click [it](https://github.com/star-inc/star_yuuki_bot/releases/tag/old-versions). +You need the "LINE Unofficial API Core for Python". (Ex:[olsb](https://github.com/star-inc/olsb_cores), [akad](https://pypi.org/project/akad), [curve](https://pypi.org/project/curve) ...) + +Please place the core directory in `./libs/` + +## Installation + ++ Check your system environment(Operating System, Requirements..) + ++ Exec: + + + pip install -Ur requirements.txt ## Usage ++ Config your connection infomations in `main.py` + ++ Exec: + + python main.py -### Notice -The helpers of the project("syb-sc", "syb-ab") have not been OpenSource. And "4syb" is a part of [StarNeptuneBOT](https://starinc.xyz/snb). +## Old Version +Maybe you want the source of history version. Click [it](https://github.com/star-inc/star_yuuki_bot/releases/tag/old-versions). ### Logo Copyright Copyright of the Image which was named "logo.png" belogs to "[ ©川原 礫/ASCII Media Works/SAO Project](https://www.aniplex.co.jp)". diff --git a/libs/__init__.py b/libs/__init__.py new file mode 100644 index 0000000..10f4e38 --- /dev/null +++ b/libs/__init__.py @@ -0,0 +1 @@ +__all__ = ['function', 'connection'] \ No newline at end of file diff --git a/libs/connection.py b/libs/connection.py new file mode 100644 index 0000000..0ec6bf5 --- /dev/null +++ b/libs/connection.py @@ -0,0 +1,58 @@ +#!/usr/bin/python3 +# coding=UTF-8 + +from .core.TalkService import Client + +from thrift.transport import THttpClient +from thrift.protocol import TCompactProtocol + +""" NC HightSpeed Lib """ +try: + from thrift.protocol import fastbinary +except: + fastbinary = None +########################################## + +class Yuuki_Connection: + """ Connection Dict Type """ + + connectInfo = { + "Host": "", + "Command_Path": "", + "LongPoll_path": "" + } + + connectHeader = { + "X-Line-Application": "", + "X-Line-Access": "", + "User-Agent": "" + } + + + +class YuukiConnect: + def __init__(self, Yuuki_Connection): + + self.host = Yuuki_Connection.connectInfo["Host"] + self.com_path = Yuuki_Connection.connectInfo["Command_Path"] + self.poll_path = Yuuki_Connection.connectInfo["LongPoll_path"] + + self.con_header = Yuuki_Connection.connectHeader + + def connect(self): + transport = THttpClient.THttpClient(self.host + self.com_path) + transport_in = THttpClient.THttpClient(self.host + self.poll_path) + + transport.setCustomHeaders(self.con_header) + transport_in.setCustomHeaders(self.con_header) + + protocol = TCompactProtocol.TCompactProtocol(transport) + protocol_in = TCompactProtocol.TCompactProtocol(transport_in) + + client = Client(protocol) + listen = Client(protocol_in) + + transport.open() + transport_in.open() + + return client, listen diff --git a/libs/data.py b/libs/data.py new file mode 100644 index 0000000..2cac25f --- /dev/null +++ b/libs/data.py @@ -0,0 +1,6 @@ +#!/usr/bin/python3 +# coding=UTF-8 + +class Yuuki_Data: + def __init__(self): + pass \ No newline at end of file diff --git a/libs/function.py b/libs/function.py new file mode 100644 index 0000000..454ac93 --- /dev/null +++ b/libs/function.py @@ -0,0 +1,192 @@ +#!/usr/bin/python3 +# coding=UTF-8 + +import os, time,\ + requests, \ + json, ntpath + +from libs.core.TalkService import * +from .connection import YuukiConnect + +class Yuuki: + def __init__(self, Seq, Yuuki_Connection, Accounts, Admin=[]): + self.Seq = Seq + self.LINE_Media_server = "https://obs.line-apps.com" + + Connect = YuukiConnect(Yuuki_Connection) + + (self.client, self.listen) = Connect.connect() + self.connectHeader = Yuuki_Connection.connectHeader + + self.MyMID = self.client.getProfile().mid + + def exit(restart=False): + if restart: + Catched = open(".cache.sh", "w") + Catched.write(sys.executable + " ./bot.py") + Catched.close() + os.system("sh .cache.sh") + os.system("rm .cache.sh") + sys.exit(0) + else: + sys.exit(0) + + def readCommandLine(self, msgs): + replymsg = "" + for msg in msgs: + replymsg = replymsg + " " + msg + return replymsg + + def checkInInvitationList(self, ncMessage): + if ncMessage.param3 == self.MyMID: + inList = True + elif "\x1e" in ncMessage.param3: + if self.MyMID in ncMessage.param3.split("\x1e"): + inList = True + else: + inList = False + else: + inList = False + return inList + + def changeGroupUrlStatus(self, group, stat): + if stat == True: + us = False + else: + us = True + group.members, group.invitee = None, None + group.preventJoinByTicket = us + self.client.send_updateGroup(self.Seq, group) + + def cleanMyGroupInvitations(self): + for cleanInvitations in self.client.getGroupIdsInvited(): + self.client.send_acceptGroupInvitation(self.Seq, cleanInvitations) + self.client.send_leaveGroup(self.Seq, cleanInvitations) + + def getContact(self, mid): + if len(mid) == len(self.MyMID) and mid[0] == "u": + try: + contactInfo = self.getContact(mid) + except: + contactInfo = False + else: + contactInfo = False + return contactInfo + + def sendToWho(self, Message): + if Message.message.toType == MIDType.USER: + return Message.message.from_ + elif Message.message.toType == MIDType.ROOM: + return Message.message.to + elif Message.message.toType == MIDType.GROUP: + return Message.message.to + + def sendText(self, toid, msg): + message = Message(to=toid, text=msg.encode('utf-8')) + self.client.send_sendMessage(self.Seq, message) + + def sendUser(self, toid, mid): + message = Message(contentType=ContentType.CONTACT, + text='', + contentMetadata={ + 'mid': mid, + 'displayName': 'LINE User', + }, + to=toid) + self.client.send_sendMessage(self.Seq, message) + + def sendMedia(self, toid, type, path): + if os.path.exists(path): + file_name = ntpath.basename(path) + file_size = len(open(path, 'rb').read()) + message = Message(to=toid, text=None) + message.contentType = type + message.contentPreview = None + message.contentMetadata = { + 'FILE_NAME': str(file_name), + 'FILE_SIZE': str(file_size), + } + if type == ContentType.FILE: + media_name = file_name + else: + media_name = 'media' + message_id = self.client.sendMessage(self.Seq, message).id + files = { + 'file': open(path, 'rb'), + } + params = { + 'name': media_name, + 'oid': message_id, + 'size': file_size, + 'type': ContentType._VALUES_TO_NAMES[type].lower(), + 'ver': '1.0', + } + data = { + 'params': json.dumps(params) + } + url = self.LINE_Media_server + '/talk/m/upload.nhn' + r = requests.post(url, headers=self.connectHeader, data=data, files=files) + if r.status_code != 201: + self.client.sendText(toid, "Error!") + + def Poll(self): + NoWork = 0 + catchedNews = [] + ncMessage = Operation() + Revision = self.client.getLastOpRevision() + while True: + try: + if NoWork == 300: + Revision = self.client.getLastOpRevision() + catchedNews = self.listen.fetchOperations(Revision, 50) + if catchedNews: + NoWork = 0 + for ncMessage in catchedNews: + if ncMessage.type == OpType.RECEIVE_MESSAGE: + self.Commands(ncMessage) + elif ncMessage.type == OpType.NOTIFIED_INVITE_INTO_GROUP: + self.Main(ncMessage) + if ncMessage.reqSeq != -1: + Revision = max(Revision, ncMessage.revision) + else: + NoWork = NoWork + 1 + except SystemExit: + self.exit() + except EOFError: + pass + except: + err1, err2, err3 = sys.exc_info() + try: + if catchedNews and ncMessage: + Finded = False + for Catched in catchedNews: + if Catched.revision == ncMessage.revision: + Finded = True + if Finded: + Revision = Catched.revision + break + if not Finded: + Revision = self.client.getLastOpRevision() + for Root in self.permission["Admin"]: + self.sendText(Root, "Star Yuuki BOT - Something was wrong...\nError:\n%s\n%s\n%s" % + (err1, err2, err3)) + except: + print("Star Yuuki BOT - Damage!\nError:\n%s\n%s\n%s" % (err1, err2, err3)) + self.exit() + + def Main(self, ncMessage): + pass + + def Commands(self, ncMessage): + if 'BOT_CHECK' in ncMessage.message.contentMetadata: + pass + elif ncMessage.message.toType == MIDType.ROOM: + self.client.send_leaveRoom(self.Seq, ncMessage.message.to) + elif ncMessage.message.contentType == ContentType.NONE: + if 'Yuuki/mid' == ncMessage.message.text: + self.sendText(self.sendToWho(ncMessage), "LINE System UserID:\n" + ncMessage.message.from_) + elif 'Yuuki/Speed' == ncMessage.message.text: + Time1 = time.time() + self.sendText(self.sendToWho(ncMessage), "Testing...") + Time2 = time.time() + self.sendText(self.sendToWho(ncMessage), "Speed:\n%ss" % (Time2 - Time1,)) \ No newline at end of file diff --git a/libs/thread_control.py b/libs/thread_control.py new file mode 100644 index 0000000..d177c03 --- /dev/null +++ b/libs/thread_control.py @@ -0,0 +1,6 @@ +#!/usr/bin/python3 +# coding=UTF-8 + +class Thread_Control: + def __init__(self): + pass \ No newline at end of file diff --git a/main.py b/main.py index e69de29..e5978b9 100644 --- a/main.py +++ b/main.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- +""" + Star Yuuki Bot - Yuuki + ~~~~~~~~~~~ + This is a main program in SYB. + It`s belong to Star Yuuki(pYthon) Bot Project of Star Neptune Bot + Version: v6.5.0 + Copyright(c) 2019 Star Inc. All Rights Reserved. +""" + +Admin = [""] + +LINE_ACCESS_KEY = "" + +########################Initializing########################## +from libs.function import Yuuki +from libs.connection import Yuuki_Connection + +Connection = Yuuki_Connection() + +Connection.connectInfo = { + 'Host': '', + 'Command_Path': '', + 'LongPoll_path': '' +} + +Connection.connectHeader = { + 'X-Line-Application': '', + 'X-Line-Access': LINE_ACCESS_KEY, + 'User-Agent': '' +} + +Seq = 0 +Accounts = [] + +Console = Yuuki(Seq, Connection, Accounts, Admin) +Console.cleanMyGroupInvitations() + +############################################################## + +###########################Start!############################# +print("Star Yuuki BOT - Start Successful!") + +if __name__ == "__main__": + Console.Poll() \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..378594b --- /dev/null +++ b/requirements.txt @@ -0,0 +1,7 @@ +certifi==2019.6.16 +chardet==3.0.4 +idna==2.8 +requests==2.22.0 +six==1.12.0 +thrift==0.11.0 +urllib3==1.25.3