mirror of
https://github.com/studio-neptune/yuuki.git
synced 2024-11-10 09:02:53 +08:00
Merge pull request #22 from star-inc/rolling
v6.5.3-alpha_RC2 from Rolling
This commit is contained in:
commit
418d412ad6
12 changed files with 98 additions and 118 deletions
26
README.md
26
README.md
|
@ -1,23 +1,27 @@
|
|||
Star Yuuki(pYthon) BOT - Yuuki
|
||||
==================
|
||||
# Star Yuuki(pYthon) BOT - Yuuki
|
||||
|
||||
![Version](https://img.shields.io/badge/v6.5.3-OpenSource-FF0033.svg) ![Series](https://img.shields.io/badge/syb-Series-7700FF.svg) ![License](https://img.shields.io/badge/license-MPL--2.0-FF6600.svg) ![Python](https://img.shields.io/badge/python-3.x-0066FF.svg) ![Platform](https://img.shields.io/badge/base_on-LINE-00DD00.svg)
|
||||
|
||||
[![Scrutinizer-CI_Build](https://scrutinizer-ci.com/g/star-inc/star_yuuki_bot/badges/build.png?b=master) ![Scrutinizer-CI_Quality](https://scrutinizer-ci.com/g/star-inc/star_yuuki_bot/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/star-inc/star_yuuki_bot/) [![Code-Inspector_Score](https://www.code-inspector.com/project/3632/score/svg) ![Code-Inspector_Score](https://www.code-inspector.com/project/3632/status/svg)](https://frontend.code-inspector.com/public/project/3632/star_yuuki_bot/dashboard)
|
||||
[![Scrutinizer-CI_Build](https://scrutinizer-ci.com/g/star-inc/star_yuuki_bot/badges/build.png?b=rolling) ![Scrutinizer-CI_Quality](https://scrutinizer-ci.com/g/star-inc/star_yuuki_bot/badges/quality-score.png?b=rolling)](https://scrutinizer-ci.com/g/star-inc/star_yuuki_bot/) [![Code-Inspector_Score](https://www.code-inspector.com/project/3632/score/svg) ![Code-Inspector_Score](https://www.code-inspector.com/project/3632/status/svg)](https://frontend.code-inspector.com/public/project/3632/star_yuuki_bot/dashboard)
|
||||
|
||||
A Python BOT for LINE Group Protection.
|
||||
![ICON](logo.png)
|
||||
|
||||
## Maybe you had seen it !
|
||||
|
||||
Yeah! This is the BOT which was the first Chinese LINE Group Security BOT.
|
||||
Now, it is OpenSource.
|
||||
|
||||
## License
|
||||
|
||||
The software licensed under [Mozilla Public License 2.0](LICENSE.md) with [COPYING.md](COPYING.md).
|
||||
|
||||
## Warning
|
||||
|
||||
Some function of old version maybe will make your LINE account be banned.
|
||||
|
||||
## Requirement
|
||||
|
||||
Recommend Environment:
|
||||
|
||||
Ubuntu >= 18.04
|
||||
|
@ -26,27 +30,29 @@ Recommend Environment:
|
|||
git >= 2.17
|
||||
|
||||
## Setup
|
||||
|
||||
+ Check your system environment(Operating System, Requirements...)
|
||||
|
||||
+ Exec:
|
||||
+ Execute:
|
||||
|
||||
|
||||
pip install -Ur requirements.txt
|
||||
`pip install -Ur requirements.txt`
|
||||
|
||||
## Usage
|
||||
|
||||
+ Copy `config.yaml` from `config.sample.yaml`
|
||||
|
||||
+ Configure your settings and environment in `config.yaml`
|
||||
|
||||
+ Exec:
|
||||
+ Execute:
|
||||
|
||||
|
||||
python main.py
|
||||
`python main.py`
|
||||
|
||||
## Old Version
|
||||
Maybe you want the source of history version. Click [it](https://github.com/star-inc/star_yuuki_bot/releases/tag/old-versions).
|
||||
|
||||
Maybe you want the source of history version. [Click me](https://github.com/star-inc/star_yuuki_bot/releases/tag/old-versions)
|
||||
|
||||
### Logo Copyright
|
||||
|
||||
Copyright of the Image which was named "logo.png" belongs to "[©川原 礫/ASCII Media Works/SAO Project](https://www.aniplex.co.jp)".
|
||||
|
||||
> (c) 2020 Star Inc.
|
||||
|
|
|
@ -1,21 +1,17 @@
|
|||
# Sample Config File for star_yuuki_bot
|
||||
# Please replace setting options from `libs/config.py`
|
||||
|
||||
Yuuki:
|
||||
SecurityService: True
|
||||
Default_Language: en
|
||||
Admin:
|
||||
-
|
||||
Hour_KickLimit: 10
|
||||
Hour_CancelLimit: 10
|
||||
GroupMebers_Demand: 10
|
||||
|
||||
LINE:
|
||||
Server:
|
||||
Host:
|
||||
Command_Path:
|
||||
LongPoll_path:
|
||||
Host: https://example.com
|
||||
Command_Path: /example_path
|
||||
LongPoll_path: /example_path
|
||||
|
||||
Account:
|
||||
X-Line-Application:
|
||||
X-Line-Access:
|
||||
User-Agent:
|
||||
|
||||
helper_LINE_ACCESS_KEYs:
|
||||
-
|
||||
X-Line-Application: LINE_Application_Identification
|
||||
X-Line-Access: LINE_Application_AccessKey
|
||||
User-Agent: LINE_Application_User-Agent
|
||||
|
|
|
@ -37,7 +37,7 @@ class Yuuki_Config:
|
|||
|
||||
systemConfig = {
|
||||
"name": "Yuuki",
|
||||
"version": "v6.5.3-alpha_RC1",
|
||||
"version": "v6.5.3-alpha_RC2",
|
||||
"version_check": True,
|
||||
"project_url": "https://tinyurl.com/syb-yuuki",
|
||||
"man_page": "https://tinyurl.com/yuuki-manual",
|
||||
|
@ -57,13 +57,13 @@ class Yuuki_Config:
|
|||
}
|
||||
|
||||
def __init__(self, config_path="config.yaml"):
|
||||
assert os.path.isfile(config_path), "The configure file, `config.yaml` was not found."
|
||||
assert os.path.isfile(config_path), "The config file, `config.yaml` was not found."
|
||||
with open(config_path, "r") as configfile:
|
||||
self.config = yaml.load(configfile, Loader=yaml.FullLoader)
|
||||
self._yuuki_config()
|
||||
|
||||
def _yuuki_config(self):
|
||||
assert self.config is not None, "Invalid configure file"
|
||||
assert self.config is not None, "Invalid config file"
|
||||
if "Yuuki" in self.config:
|
||||
for key in self.config["Yuuki"]:
|
||||
if key in self.systemConfig:
|
||||
|
|
|
@ -9,7 +9,7 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|||
from thrift.protocol import TCompactProtocol
|
||||
from thrift.transport import THttpClient
|
||||
|
||||
from yuuki_core.TalkService import Client
|
||||
from yuuki_core.TalkService import Client, TalkException
|
||||
|
||||
# NC HighSpeed Library
|
||||
try:
|
||||
|
@ -21,16 +21,14 @@ except ImportError:
|
|||
class Yuuki_Connect:
|
||||
def __init__(self, Yuuki_Configs):
|
||||
|
||||
self.helper = {}
|
||||
|
||||
self.host = Yuuki_Configs.connectInfo["Host"]
|
||||
self.com_path = Yuuki_Configs.connectInfo["Command_Path"]
|
||||
self.poll_path = Yuuki_Configs.connectInfo["LongPoll_path"]
|
||||
|
||||
self.con_header = Yuuki_Configs.connectHeader
|
||||
|
||||
self.helper = []
|
||||
self.helper_ids = []
|
||||
self.helper_authTokens = {}
|
||||
|
||||
def connect(self, listen_timeout=600000):
|
||||
transport = THttpClient.THttpClient(self.host + self.com_path)
|
||||
transport_in = THttpClient.THttpClient(self.host + self.poll_path)
|
||||
|
@ -51,41 +49,20 @@ class Yuuki_Connect:
|
|||
|
||||
return client, listen
|
||||
|
||||
def helperConnect(self, LINE_ACCESS_KEY):
|
||||
helper_ConnectHeader = self.con_header.copy()
|
||||
helper_ConnectHeader["X-Line-Access"] = LINE_ACCESS_KEY
|
||||
def helperConnect(self, auth_token):
|
||||
helper_connect_header = self.con_header.copy()
|
||||
helper_connect_header["X-Line-Access"] = auth_token
|
||||
|
||||
transport = THttpClient.THttpClient(self.host + self.com_path)
|
||||
transport.setCustomHeaders(helper_ConnectHeader)
|
||||
transport.setCustomHeaders(helper_connect_header)
|
||||
protocol = TCompactProtocol.TCompactProtocol(transport)
|
||||
client = Client(protocol)
|
||||
transport.open()
|
||||
|
||||
# noinspection PyBroadException
|
||||
try:
|
||||
profile = client.getProfile()
|
||||
|
||||
self.helper.append(client)
|
||||
self.helper_ids.append(profile.mid)
|
||||
self.helper_authTokens[profile.mid] = LINE_ACCESS_KEY
|
||||
|
||||
self.helper[profile.mid] = client
|
||||
return True
|
||||
except:
|
||||
print("Error:\n%s\nNot Acceptable\n" % (LINE_ACCESS_KEY,))
|
||||
|
||||
def helperThreadConnect(self, userId):
|
||||
if userId in self.helper_authTokens:
|
||||
LINE_ACCESS_KEY = self.helper_authTokens.get(userId)
|
||||
else:
|
||||
return None
|
||||
|
||||
helper_ConnectHeader = self.con_header.copy()
|
||||
helper_ConnectHeader["X-Line-Access"] = LINE_ACCESS_KEY
|
||||
|
||||
transport = THttpClient.THttpClient(self.host + self.com_path)
|
||||
transport.setCustomHeaders(helper_ConnectHeader)
|
||||
protocol = TCompactProtocol.TCompactProtocol(transport)
|
||||
client = Client(protocol)
|
||||
transport.open()
|
||||
|
||||
return client
|
||||
except TalkException:
|
||||
print("Error:\n%s\nNot Acceptable\n" % (auth_token,))
|
||||
|
|
17
libs/data.py
17
libs/data.py
|
@ -82,25 +82,16 @@ class Yuuki_Data:
|
|||
for data_type in self.DataType:
|
||||
name = self.DataPath + self.DataName.format(data_type)
|
||||
|
||||
test_result = 0
|
||||
if not os.path.isfile(name):
|
||||
with open(name, "w") as f:
|
||||
f.write("")
|
||||
self.Data[data_type] = self.DataType[data_type]
|
||||
json.dump(f, self.Data[data_type])
|
||||
else:
|
||||
with open(name, "r") as f:
|
||||
try:
|
||||
json.load(f)
|
||||
self.Data[data_type] = json.load(f)
|
||||
except ValueError:
|
||||
test_result = 1
|
||||
assert test_result == 0, "{}\nJson Test Error".format(name)
|
||||
|
||||
with open(name, "r+") as f:
|
||||
text = f.read()
|
||||
if text != "":
|
||||
self.Data[data_type] = json.loads(text)
|
||||
else:
|
||||
self.Data[data_type] = self.DataType[data_type]
|
||||
f.write(json.dumps(self.Data[data_type]))
|
||||
assert "{}\nJson Test Error".format(name)
|
||||
|
||||
return self._MDS_Initialize()
|
||||
|
||||
|
|
|
@ -105,7 +105,7 @@ class PythonMDS:
|
|||
return {"status": 200}
|
||||
|
||||
def _yuuki_limit_decrease(self, data):
|
||||
self.switch_data["LimitInfo"][data["path"]][data["userId"]] -= 1
|
||||
self.switch_data["LimitInfo"][data["path"]][data["data"]] -= 1
|
||||
return {"status": 200}
|
||||
|
||||
def _shutdown(self, data):
|
||||
|
|
|
@ -56,7 +56,7 @@ class Yuuki_Command:
|
|||
GroupPrivilege = self.Yuuki.Admin + [self.Yuuki_StaticTools.sybGetGroupCreator(GroupInfo).mid] + \
|
||||
self.Yuuki.data.getGroup(GroupInfo.id)["Ext_Admin"]
|
||||
if ncMessage.message.from_ in GroupPrivilege:
|
||||
for userId in self.Yuuki.Connect.helper_ids:
|
||||
for userId in self.Yuuki.Connect.helper:
|
||||
self.Yuuki_DynamicTools.sendUser(
|
||||
self.Yuuki_StaticTools.sendToWho(ncMessage), userId)
|
||||
|
||||
|
@ -210,7 +210,9 @@ class Yuuki_Command:
|
|||
for userId in self.Yuuki.data.getGroup(GroupInfo.id)["Ext_Admin"]:
|
||||
if userId not in status_added:
|
||||
status += "{}: {}\n".format(
|
||||
self.Yuuki.get_text("Unknown"), userId)
|
||||
self.Yuuki.get_text("Unknown"),
|
||||
userId
|
||||
)
|
||||
self.Yuuki_DynamicTools.sendText(
|
||||
self.Yuuki_StaticTools.sendToWho(ncMessage),
|
||||
status + self.Yuuki.get_text("\nExtend Administrator(s)")
|
||||
|
@ -281,7 +283,7 @@ class Yuuki_Command:
|
|||
ncMessage), self.Yuuki.get_text("Bye Bye"))
|
||||
self.Yuuki_DynamicTools.getClient(
|
||||
self.Yuuki.MyMID).leaveGroup(self.Yuuki.Seq, GroupInfo.id)
|
||||
for userId in self.Yuuki.Connect.helper_ids:
|
||||
for userId in self.Yuuki.Connect.helper:
|
||||
if userId in [member.mid for member in GroupInfo.members]:
|
||||
self.Yuuki_DynamicTools.getClient(
|
||||
userId).leaveGroup(self.Yuuki.Seq, GroupInfo.id)
|
||||
|
|
|
@ -58,7 +58,7 @@ class Yuuki_JoinGroup:
|
|||
|
||||
def _helper_check(self, ncMessage, GroupInvite, BlockedIgnore):
|
||||
if ncMessage.param1 in self.Yuuki.data.getData(["Global", "GroupJoined"]) and not BlockedIgnore:
|
||||
for userId in self.Yuuki.Connect.helper_ids:
|
||||
for userId in self.Yuuki.Connect.helper:
|
||||
if self.Yuuki_DynamicTools.checkInInvitationList(ncMessage, userId) or userId in GroupInvite:
|
||||
self.Yuuki_DynamicTools.getClient(userId).acceptGroupInvitation(self.Yuuki.Seq, ncMessage.param1)
|
||||
self.Yuuki_DynamicTools.getGroupTicket(ncMessage.param1, userId, True)
|
||||
|
|
|
@ -12,6 +12,14 @@ from yuuki_core.ttypes import OpType
|
|||
from ..tools import Yuuki_StaticTools, Yuuki_DynamicTools
|
||||
|
||||
|
||||
def security_access_checker(function):
|
||||
def wrapper(*args):
|
||||
if not args[2].get("Security_Access"):
|
||||
return
|
||||
return function(*args)
|
||||
return wrapper
|
||||
|
||||
|
||||
class Yuuki_Security:
|
||||
def __init__(self, Yuuki):
|
||||
"""
|
||||
|
@ -26,9 +34,10 @@ class Yuuki_Security:
|
|||
self.Yuuki_StaticTools = Yuuki_StaticTools()
|
||||
self.Yuuki_DynamicTools = Yuuki_DynamicTools(self.Yuuki)
|
||||
|
||||
@security_access_checker
|
||||
def _NOTIFIED_UPDATE_GROUP(self, GroupInfo, SecurityInfo, ncMessage):
|
||||
if SecurityInfo["Another"] == '4':
|
||||
if not GroupInfo.preventJoinByTicket and SecurityInfo["Action"] not in self.Yuuki.Connect.helper_ids:
|
||||
if not GroupInfo.preventJoinByTicket and SecurityInfo["Action"] not in self.Yuuki.Connect.helper:
|
||||
self.Yuuki.threadExec(
|
||||
self.Yuuki_DynamicTools.changeGroupUrlStatus,
|
||||
(GroupInfo, False)
|
||||
|
@ -49,12 +58,13 @@ class Yuuki_Security:
|
|||
ncMessage.type
|
||||
))
|
||||
|
||||
@security_access_checker
|
||||
def _NOTIFIED_INVITE_INTO_GROUP(self, GroupInfo, SecurityInfo, ncMessage):
|
||||
Canceler = "None"
|
||||
if "\x1e" in SecurityInfo["Another"]:
|
||||
Canceler = self._NOTIFIED_INVITE_INTO_GROUP_list(GroupInfo, SecurityInfo, ncMessage, Canceler)
|
||||
elif SecurityInfo["Another"] not in self.Yuuki.AllAccountIds + SecurityInfo["GroupPrivilege"]:
|
||||
Canceler = self._NOTIFIED_INVITE_INTO_GROUP_single(GroupInfo, SecurityInfo, ncMessage, Canceler)
|
||||
Canceler = self._NOTIFIED_INVITE_INTO_GROUP_single(GroupInfo, SecurityInfo, ncMessage)
|
||||
if Canceler != "None":
|
||||
self.Yuuki_DynamicTools.sendText(
|
||||
SecurityInfo["GroupID"],
|
||||
|
@ -89,7 +99,7 @@ class Yuuki_Security:
|
|||
))
|
||||
return Canceler
|
||||
|
||||
def _NOTIFIED_INVITE_INTO_GROUP_single(self, GroupInfo, SecurityInfo, ncMessage, Canceler):
|
||||
def _NOTIFIED_INVITE_INTO_GROUP_single(self, GroupInfo, SecurityInfo, ncMessage):
|
||||
if GroupInfo.invitee and SecurityInfo["Another"] in [user.mid for user in GroupInfo.invitee]:
|
||||
Canceler = self.Yuuki_DynamicTools.modifyGroupMemberList(2, GroupInfo, SecurityInfo["Another"])
|
||||
else:
|
||||
|
@ -113,6 +123,7 @@ class Yuuki_Security:
|
|||
))
|
||||
return Canceler
|
||||
|
||||
@security_access_checker
|
||||
def _NOTIFIED_ACCEPT_GROUP_INVITATION(self, GroupInfo, SecurityInfo, ncMessage):
|
||||
for userId in self.Yuuki.data.getData(["BlackList"]):
|
||||
if userId == SecurityInfo["Action"]:
|
||||
|
@ -133,7 +144,7 @@ class Yuuki_Security:
|
|||
))
|
||||
|
||||
def _NOTIFIED_KICKOUT_FROM_GROUP(self, GroupInfo, SecurityInfo, ncMessage):
|
||||
if SecurityInfo["Action"] in self.Yuuki.Connect.helper_ids:
|
||||
if SecurityInfo["Action"] in self.Yuuki.Connect.helper:
|
||||
# Log
|
||||
self.Yuuki.data.updateLog("KickEvent", (
|
||||
self.Yuuki.data.getTime(),
|
||||
|
|
|
@ -116,17 +116,10 @@ class Yuuki_DynamicTools:
|
|||
:param userId: string
|
||||
:return: TalkServiceClient
|
||||
"""
|
||||
if self.Yuuki.Threading:
|
||||
if userId == self.Yuuki.MyMID:
|
||||
(client, _) = self.Yuuki.Connect.connect()
|
||||
return client
|
||||
else:
|
||||
return self.Yuuki.Connect.helperThreadConnect(userId)
|
||||
if userId == self.Yuuki.MyMID:
|
||||
return self.Yuuki.client
|
||||
else:
|
||||
Accounts = [self.Yuuki.client] + self.Yuuki.Connect.helper
|
||||
for count, AccountUserId in enumerate(self.Yuuki.AllAccountIds):
|
||||
if AccountUserId == userId:
|
||||
return Accounts[count]
|
||||
return self.Yuuki.Connect.helper[userId]
|
||||
|
||||
def checkInInvitationList(self, ncMessage, userId=None):
|
||||
"""
|
||||
|
|
|
@ -35,7 +35,7 @@ class Yuuki_WebAdminAPI:
|
|||
def get_helpers(self, data):
|
||||
if data:
|
||||
pass
|
||||
return self.Yuuki.Connect.helper_ids
|
||||
return self.Yuuki.Connect.helper
|
||||
|
||||
def get_logs(self, data):
|
||||
return self.Yuuki_DataHandle.get_logs(data)
|
||||
|
|
|
@ -50,16 +50,13 @@ class Yuuki:
|
|||
origin_url = "https://github.com/star-inc/star_yuuki_bot.git"
|
||||
|
||||
if self.YuukiConfigs["version_check"]:
|
||||
# noinspection PyBroadException
|
||||
try:
|
||||
git_remote = Repo('.').remote()
|
||||
update_status = git_remote.fetch()[0]
|
||||
if update_status.flags == 64:
|
||||
git_result = "New version found."
|
||||
elif update_status.flags == 4:
|
||||
git_result = "This is the latest version."
|
||||
except:
|
||||
git_result = "Something was wrong."
|
||||
git_remote = Repo('.').remote()
|
||||
update_status = git_remote.fetch()[0]
|
||||
if update_status.flags == 64:
|
||||
git_result = "New version found."
|
||||
elif update_status.flags == 4:
|
||||
git_result = "This is the latest version."
|
||||
origin_url = "\n".join(git_remote.urls)
|
||||
|
||||
return self._Announce_Dog(git_result, origin_url)
|
||||
|
||||
|
@ -68,8 +65,8 @@ class Yuuki:
|
|||
"\n{} {}\n"
|
||||
"\t===\n\n"
|
||||
"<*> {}\n\n"
|
||||
"More Information:\n"
|
||||
"{}\n\n\t\t\t\t\t"
|
||||
"Update Origin:\n"
|
||||
"\t{}\n\n\t\t\t\t\t"
|
||||
"{}\n\t{}\n".format(
|
||||
self.YuukiConfigs["name"],
|
||||
self.YuukiConfigs["version"],
|
||||
|
@ -107,7 +104,10 @@ class Yuuki:
|
|||
self.MyMID = self.profile.mid
|
||||
self.revision = self.client.getLastOpRevision()
|
||||
|
||||
self.AllAccountIds = [self.MyMID] + self.Connect.helper_ids
|
||||
self.AllAccountIds = [self.MyMID]
|
||||
|
||||
for userId in self.Connect.helper:
|
||||
self.AllAccountIds.append(userId)
|
||||
|
||||
if len(self.data.getData(["LimitInfo"])) != 2:
|
||||
self.data.updateData(["LimitInfo"], self.data.LimitType)
|
||||
|
@ -144,20 +144,24 @@ class Yuuki:
|
|||
time.sleep(1)
|
||||
self.data.MdsThreadControl.stop("wa_listen")
|
||||
if restart:
|
||||
if platform.system() == "Windows":
|
||||
with open("cache.bat", "w") as c:
|
||||
c.write(sys.executable + " ./main.py")
|
||||
os.system("cache.bat")
|
||||
os.system("del cache.bat")
|
||||
elif platform.system() == "Linux":
|
||||
with open(".cache.sh", "w") as c:
|
||||
c.write(sys.executable + " ./main.py")
|
||||
os.system("sh .cache.sh")
|
||||
os.system("rm .cache.sh")
|
||||
else:
|
||||
print("Star Yuuki BOT - Restart Error\n\nUnknown Platform")
|
||||
self.__restart()
|
||||
sys.exit(0)
|
||||
|
||||
@staticmethod
|
||||
def __restart():
|
||||
if platform.system() == "Windows":
|
||||
with open("cache.bat", "w") as c:
|
||||
c.write(sys.executable + " ./main.py")
|
||||
os.system("cache.bat")
|
||||
os.system("del cache.bat")
|
||||
elif platform.system() == "Linux":
|
||||
with open(".cache.sh", "w") as c:
|
||||
c.write(sys.executable + " ./main.py")
|
||||
os.system("sh .cache.sh")
|
||||
os.system("rm .cache.sh")
|
||||
else:
|
||||
print("Star Yuuki BOT - Restart Error\n\nUnknown Platform")
|
||||
|
||||
def threadExec(self, function, args):
|
||||
if self.Threading:
|
||||
self.Thread_Control.add(function, args)
|
||||
|
|
Loading…
Reference in a new issue