scala port stage1 (not tested)

This commit is contained in:
A.C.Sukazyo Eyre 2023-09-05 14:14:01 +08:00
parent 213798dab7
commit aafdcc1fb2
Signed by: Eyre_S
GPG Key ID: C17CE40291207874
107 changed files with 1556 additions and 2163 deletions

View File

@ -10,6 +10,8 @@
#build
/build/
/bin/
.metals/
.bloop/
.project
lcoal.properties

2
.gitignore vendored
View File

@ -10,6 +10,8 @@
#build
/build/
/bin/
.metals/
.bloop/
.project
lcoal.properties

View File

@ -1,5 +1,5 @@
plugins {
id 'java'
id 'scala'
id 'java-library'
id 'application'
id 'maven-publish'
@ -50,7 +50,9 @@ final long proj_code_time = proj_clean ? grgit.head().dateTime.toInstant().toEpo
final JavaVersion proj_java = JavaVersion.VERSION_17
final Charset proj_file_encoding = StandardCharsets.UTF_8
final proj_scala_api = 3
//final proj_scala_lib = proj_scala_api+'.4.0-RC1-bin-20230901-89e8dba-NIGHTLY'
final proj_scala_lib = proj_scala_api+'.3.1-RC7'
String publish_local_url = null
String publish_remote_url = null
String publish_remote_username = null
@ -72,6 +74,7 @@ repositories {
dependencies {
api "org.scala-lang:scala3-library_3:${proj_scala_lib}"
compileOnlyApi "com.github.spotbugs:spotbugs-annotations:${lib_spotbugs_v}"
implementation "cc.sukazyo:messiva:${lib_messiva_v}"
@ -86,8 +89,30 @@ dependencies {
}
application {
mainClass = proj_application_main
sourceSets {
main {
scala { srcDirs = ['src/main/scala', 'src/main/old'] }
}
}
scala {
compileJava {
sourceCompatibility '17'
targetCompatibility '17'
options.encoding = proj_file_encoding.name()
}
compileScala {
options.encoding = proj_file_encoding.name()
scalaCompileOptions.encoding = proj_file_encoding.name()
}
}
test {
@ -97,27 +122,8 @@ test {
}
}
java {
sourceCompatibility proj_java
targetCompatibility proj_java
withSourcesJar()
}
tasks.withType(JavaCompile).configureEach {
options.encoding = proj_file_encoding.name()
}
tasks.withType(Javadoc).configureEach {
options.encoding = proj_file_encoding.name()
options.docEncoding = proj_file_encoding.name()
options.charSet = proj_file_encoding.name()
}
tasks.test {
useJUnitPlatform()
application {
mainClass = proj_application_main
}
buildConfig {

View File

@ -7,8 +7,8 @@ MORNY_COMMIT_PATH = https://github.com/Eyre-S/Coeur-Morny-Cono/commit/%s
VERSION = 1.0.0-RC4
USE_DELTA = false
VERSION_DELTA =
USE_DELTA = true
VERSION_DELTA = scalaport1
CODENAME = beiping

View File

@ -1,296 +0,0 @@
package cc.sukazyo.cono.morny;
import cc.sukazyo.cono.morny.util.CommonFormat;
import javax.annotation.Nonnull;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.List;
import static cc.sukazyo.cono.morny.Log.logger;
/**
* 程序启动入口<br>
* <br>
* 会处理程序传入的参数和选项等数据并执行对应的启动方式<br>
*
* @since 0.4.0.0
*/
public class ServerMain {
public static final long systemStartupTime = System.currentTimeMillis();
private static final String THREAD_MORNY_INIT = "morny-init";
/**
* 程序入口也是参数处理器<br>
* <br>
* {@code -} 开头的参数会被解析为选项<br>
* <br>
* 支持以下选项
* <ul>
* <li>
* {@code --version} 只输出版本信息不运行主程序此参数会导致其它所有参数失效优先级最高
* </li>
* <li>
* {@code --only-hello} 只输出欢迎字符画({@link MornyAbout#MORNY_PREVIEW_IMAGE_ASCII})不运行主程序
* 不要同时使用 {@code --no-hello}原因见下
* </li>
* <li>
* {@code --token} <b>主程序模式的必选项</b><br>
* 用于 bot 启动的 telegram bot api token
* </li>
* <li>
* {@code --username} {@link MornyCoeur#getUsername() bot username} 预定义
* </li>
* <li>
* {@code --api} 设定 {@link MornyCoeur#getAccount() bot client} 使用的 telegram bot api server
* 需要注意的是如果带有后缀 {@code /bot} 则会单独设定 api server
* 而不会适应性的同时为 {@code --api-files} 设定值
* </li>
* <li>
* {@code --api-files} 单独设定 {@link MornyCoeur#getAccount() bot client} 使用的 telegram bot file api server
* </li>
* <li>
* {@code --report-to} 设定 {@link cc.sukazyo.cono.morny.daemon.MornyReport} 的运行报告要发送到的 telegram 频道
* </li>
* <li>
* {@code --no-hello} 不在主程序启动时输出用于欢迎消息的字符画
* {@code --only-hello} 参数不兼容 会导致程序完全没有任何输出
* </li>
* <li>
* {@code --outdated-block} 会使得 {@link MornyConfig#eventIgnoreOutdated}
* 赋值为程序启动的时间从而造成阻挡程序启动之前的消息事件处理效果
* </li>
* <li>
* {@code --auto-cmd} (下面两个)选项 {@code --auto-cmd-list} {@code --auto-cmd-remove} 的合并版本
* </li>
* <li>
* {@code --auto-cmd-list} 使 morny 在启动时自动依据程序本体更新登录 bot 的命令列表
* </li>
* <li>
* {@code --auto-cmd-remove} 使 morny 在关闭时自动依据程序本体删除 bot 的命令列表
* </li>
* </ul>
* <s>除去选项之外第一个参数会被赋值为 bot telegram bot api token</s>
* <s>第二个参数会被赋值为 bot username 限定名其余的参数会被认定为无法理解</s><br>
* <b> {@code 0.4.2.3}token username 的赋值已被选项组支持</b><br>
* <b> {@code 0.5.0.4}旧的直接通过参数为 bot token & username 赋值的方式已被删除</b>
* 使用参数所进行取值的 token username 已被转移至 {@code --token} {@code --username} 参数<br>
*
* @see MornyCoeur#init
* @since 0.4.0.0
* @param args 参数组
*/
public static void main (@Nonnull String[] args) {
//#
//# 启动参数设置区块
//#
final MornyConfig.Prototype config = new MornyConfig.Prototype();
boolean versionEchoMode = false;
boolean welcomeEchoMode = false;
boolean showWelcome = true;
config.eventOutdatedTimestamp = systemStartupTime;
List<String> unknownArgs = new ArrayList<>();
//# 从命令行参数设置启动参数
for (int i = 0; i < args.length; i++) {
if (args[i].startsWith("-")) {
switch (args[i]) {
case "-d", "--dbg", "--debug" -> {
Log.debug(true);
continue;
}
case "--outdated-block", "-ob" -> {
config.eventIgnoreOutdated = true;
continue;
}
case "--no-hello", "-hf", "--quiet", "-q" -> {
showWelcome = false;
continue;
}
case "--only-hello", "-ho", "-o", "-hi" -> {
welcomeEchoMode = true;
continue;
}
case "--version", "-v" -> {
versionEchoMode = true;
continue;
}
case "--token", "-t" -> {
i++;
config.telegramBotKey = args[i];
continue;
}
case "--username", "-u" -> {
i++;
config.telegramBotUsername = args[i];
continue;
}
case "--master", "-mm" -> {
i++;
config.trustedMaster = Long.parseLong(args[i]);
continue;
}
case "--trusted-chat", "-trs" -> {
i++;
config.trustedChat = Long.parseLong(args[i]);
continue;
}
// noinspection SpellCheckingInspection
case "--trusted-reader-dinner", "-trsd" -> {
i++;
config.dinnerTrustedReaders.add(Long.parseLong(args[i]));
continue;
}
case "--dinner-chat", "-chd" -> {
i++;
config.dinnerChatId = Long.parseLong(args[i]);
continue;
}
case "--auto-cmd", "-cmd", "-c" -> {
config.commandLoginRefresh = true;
config.commandLogoutClear = true;
continue;
}
case "--auto-cmd-list", "-ca" -> {
config.commandLoginRefresh = true;
continue;
}
case "--auto-cmd-remove", "-cr" -> {
config.commandLogoutClear = true;
continue;
}
case "--api", "-a" -> {
i++;
config.telegramBotApiServer = args[i];
continue;
}
case "--api-files", "files-api", "-af" -> {
i++;
config.telegramBotApiServer4File = args[i];
continue;
}
case "--report-to" -> {
i++;
config.reportToChat = Long.parseLong(args[i]);
continue;
}
case "--medication-notify-chat", "-medc" -> {
i++;
config.medicationNotifyToChat = Long.parseLong(args[i]);
continue;
}
case "--medication-notify-timezone", "-medtz" -> {
i++;
config.medicationTimerUseTimezone = ZoneOffset.ofHours(Integer.parseInt(args[i]));
continue;
}
case "--medication-notify-times", "-medt" -> {
i++;
for (String u : args[i].split(","))
config.medicationNotifyAt.add(Integer.parseInt(u));
continue;
}
}
}
unknownArgs.add(args[i]);
}
//# 从环境变量设置启动参数
String propToken = null;
String propTokenKey = null;
for (String iKey : MornyConfig.PROP_TOKEN_KEY) {
if (System.getenv(iKey) != null) {
propToken = System.getenv(iKey);
propTokenKey = iKey;
}
}
//#
//# 启动信息输出
//# 启动相关参数的检查和处理
//#
if (showWelcome) logger.info(MornyAbout.MORNY_PREVIEW_IMAGE_ASCII);
if (welcomeEchoMode) return;
unknownArgs.forEach(arg -> logger.warn("Can't understand arg to some meaning :\n " + arg));
if (Log.debug())
logger.warn("Debug log output enabled.\n It may lower your performance, make sure that you are not in production environment.");
logger.debug("Debug log output enabled.");
if (versionEchoMode) {
logger.info(String.format("""
Morny Cono Version
- version :
Morny %s
%s%s
- md5hash :
%s
- gitstat :
%s
- co.time :
%d
%s [UTC]""",
MornySystem.CODENAME.toUpperCase(),
MornySystem.VERSION_BASE,
MornySystem.isUseDelta() ? ""+MornySystem.VERSION_DELTA : "",
MornySystem.getJarMd5(),
MornySystem.isGitBuild() ? (String.format(
"on commit %s\n %s",
MornySystem.isCleanBuild() ? "- clean-build" : "<δ/non-clean-build>",
BuildConfig.COMMIT
)) : "<non-git-build>",
BuildConfig.CODE_TIMESTAMP,
CommonFormat.formatDate(BuildConfig.CODE_TIMESTAMP, 0)
));
return;
}
logger.info(String.format("""
ServerMain.java Loaded >>>
- version %s
- Morny %s
- <%s> [%d]""",
MornySystem.VERSION_FULL,
MornySystem.CODENAME.toUpperCase(),
MornySystem.getJarMd5(), BuildConfig.CODE_TIMESTAMP
));
//#
//# Coeur 参数检查和正式启动主程序
//#
if (propToken != null) {
config.telegramBotKey = propToken;
logger.info("Parameter <token> set by EnvVar $"+propTokenKey);
}
Thread.currentThread().setName(THREAD_MORNY_INIT);
try {
MornyCoeur.init(new MornyConfig(config));
} catch (MornyConfig.CheckFailure.NullTelegramBotKey ignore) {
logger.info("Parameter required has no value:\n --token.");
} catch (MornyConfig.CheckFailure e) {
logger.error("Unknown failure occurred while starting ServerMain!:");
e.printStackTrace(System.out);
}
}
}

View File

@ -1,58 +0,0 @@
package cc.sukazyo.cono.morny.bot.command;
import cc.sukazyo.cono.morny.MornyCoeur;
import cc.sukazyo.cono.morny.util.tgapi.InputCommand;
import com.pengrad.telegrambot.model.Chat;
import com.pengrad.telegrambot.model.Update;
import com.pengrad.telegrambot.request.DeleteMessage;
import com.pengrad.telegrambot.request.GetChatMember;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import static cc.sukazyo.cono.morny.Log.logger;
public class DirectMsgClear implements ISimpleCommand {
@Nonnull @Override public String getName () { return "r"; }
@Nullable @Override public String[] getAliases () { return new String[0]; }
@Override
public void execute (@Nonnull InputCommand command, @Nonnull Update event) {
logger.debug("Executing command /r");
if (event.message().replyToMessage() == null) return;
logger.trace("Message is a reply");
if (event.message().replyToMessage().from().id() != MornyCoeur.getUserid()) return;
logger.trace("Message is from me");
if (System.currentTimeMillis()/1000 - event.message().replyToMessage().date() > 48*60*60) return;
logger.trace("Message is not older than 48 hours");
final boolean isTrusted = MornyCoeur.trustedInstance().isTrusted(event.message().from().id());
if (
isTrusted || (
event.message().replyToMessage().replyToMessage() != null &&
event.message().replyToMessage().replyToMessage().from().id().equals(event.message().from().id())
)
) {
MornyCoeur.extra().exec(new DeleteMessage(
event.message().chat().id(), event.message().replyToMessage().messageId()
));
if (event.message().chat().type() == Chat.Type.Private || (
MornyCoeur.extra().exec(
new GetChatMember(event.message().chat().id(), event.message().from().id())
).chatMember().canDeleteMessages()
)) {
MornyCoeur.extra().exec(new DeleteMessage(
event.message().chat().id(), event.message().messageId()
));
}
} else logger.trace("User is not trusted");
}
}

View File

@ -1,208 +0,0 @@
package cc.sukazyo.cono.morny.bot.command;
import cc.sukazyo.cono.morny.MornyCoeur;
import cc.sukazyo.cono.morny.daemon.MornyReport;
import cc.sukazyo.cono.morny.data.TelegramStickers;
import cc.sukazyo.cono.morny.util.CommonConvert;
import cc.sukazyo.cono.morny.util.CommonEncrypt;
import cc.sukazyo.cono.morny.util.tgapi.InputCommand;
import cc.sukazyo.cono.morny.util.tgapi.formatting.MsgEscape;
import com.pengrad.telegrambot.model.PhotoSize;
import com.pengrad.telegrambot.model.Update;
import com.pengrad.telegrambot.model.request.ParseMode;
import com.pengrad.telegrambot.request.GetFile;
import com.pengrad.telegrambot.request.SendDocument;
import com.pengrad.telegrambot.request.SendMessage;
import com.pengrad.telegrambot.request.SendSticker;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
import java.util.Base64;
import static cc.sukazyo.cono.morny.Log.logger;
public class Encryptor implements ITelegramCommand {
@Nonnull @Override public String getName () { return "encrypt"; }
@Nullable @Override public String[] getAliases () { return new String[0]; }
@Nonnull @Override public String getParamRule () { return "[algorithm|(l)] [(uppercase)]"; }
@Nonnull @Override public String getDescription () { return "通过指定算法加密回复的内容 (目前只支持文本)"; }
@Override
public void execute (@Nonnull InputCommand command, @Nonnull Update event) {
// show a simple help page
// the first paragraph lists available encrypt algorithms, and its aliases.
// with the separator "---",
// the second paragraphs shows the mods available and its aliases.
if (!command.hasArgs() || (command.getArgs()[0].equals("l") && command.getArgs().length==1)) {
MornyCoeur.extra().exec(new SendMessage(
event.message().chat().id(), """
<b><u>base64</u></b>, b64
<b><u>base64url</u></b>, base64u, b64u
<b><u>base64decode</u></b>, base64d, b64d
<b><u>base64url-decode</u></b>, base64ud, b64ud
<b><u>sha1</u></b>
<b><u>sha256</u></b>
<b><u>sha512</u></b>
<b><u>md5</u></b>
---
<b><i>uppercase</i></b>, upper, u <i>(sha1/sha256/sha512/md5 only)</i>
"""
).replyToMessageId(event.message().messageId()).parseMode(ParseMode.HTML));
return;
}
// param1 is the encrypting algorithm, it MUST EXIST.
// so the mod will be set in param2.
// and for now only support UPPERCASE mod, so it exists in param2, or there should no any params.
boolean modUpperCase = false;
if (command.getArgs().length > 1) {
if (command.getArgs().length < 3 && (
command.getArgs()[1].equalsIgnoreCase("uppercase") ||
command.getArgs()[1].equalsIgnoreCase("u") ||
command.getArgs()[1].equalsIgnoreCase("upper")
)) {
modUpperCase = true;
} else {
MornyCoeur.extra().exec(new SendSticker(
event.message().chat().id(), TelegramStickers.ID_404
).replyToMessageId(event.message().messageId()));
return;
}
}
// for now, only support reply to A TEXT MESSAGE or ONE UNIVERSAL FILE
// if the replied message contains a UNIVERSAL FILE, it will use the file and will not use the text with it
// do not support TELEGRAM INLINE IMAGE/VIDEO/AUDIO yet
// do not support MULTI_FILE yet
// if there's no text message in reply, it will report null as result.
boolean inputText;
byte[] data;
String dataName;
if (event.message().replyToMessage() != null && event.message().replyToMessage().document() != null) {
inputText = false;
try {
data = MornyCoeur.getAccount().getFileContent(MornyCoeur.extra().exec(new GetFile(
event.message().replyToMessage().document().fileId()
)).file());
} catch (IOException e) {
logger.warn("NetworkRequest error: TelegramFileAPI:\n\t" + e.getMessage());
MornyReport.exception(e, "NetworkRequest error: TelegramFileAPI");
MornyCoeur.extra().exec(new SendSticker(
event.message().chat().id(),
TelegramStickers.ID_NETWORK_ERR
).replyToMessageId(event.message().messageId()));
return;
}
dataName = event.message().replyToMessage().document().fileName();
} else if (event.message().replyToMessage() != null && event.message().replyToMessage().photo() != null) {
inputText = false;
try {
PhotoSize originPhoto = null;
long photoSize = 0;
for (PhotoSize size : event.message().replyToMessage().photo()) if (photoSize < (long)size.width() *size.height()) {
originPhoto = size;
photoSize = (long)size.width() *size.height();
} // found max size (original) image in available sizes
if (originPhoto==null) throw new IOException("no photo object from api.");
data = MornyCoeur.getAccount().getFileContent(MornyCoeur.extra().exec(new GetFile(
originPhoto.fileId()
)).file());
} catch (IOException e) {
logger.warn("NetworkRequest error: TelegramFileAPI:\n\t" + e.getMessage());
MornyReport.exception(e, "NetworkRequest error: TelegramFileAPI");
MornyCoeur.extra().exec(new SendSticker(
event.message().chat().id(),
TelegramStickers.ID_NETWORK_ERR
).replyToMessageId(event.message().messageId()));
return;
}
dataName = "photo"+CommonConvert.byteArrayToHex(CommonEncrypt.hashMd5(String.valueOf(System.currentTimeMillis()))).substring(32-12).toUpperCase()+".png";
} else if (event.message().replyToMessage() != null && event.message().replyToMessage().text() != null) {
inputText = true;
data = event.message().replyToMessage().text().getBytes(CommonEncrypt.ENCRYPT_STANDARD_CHARSET);
dataName = null;
} else {
MornyCoeur.extra().exec(new SendMessage(
event.message().chat().id(),
"<i><u>null</u></i>"
).replyToMessageId(event.message().messageId()).parseMode(ParseMode.HTML));
return;
}
boolean echoString = true;
String resultString = null;
byte[] result = null;
String resultName = null;
switch (command.getArgs()[0]) {
case "base64", "b64", "base64url", "base64u", "b64u" -> {
final Base64.Encoder b64tool = command.getArgs()[0].contains("u") ? Base64.getUrlEncoder() : Base64.getEncoder();
result = b64tool.encode(data);
if (!inputText) {
echoString = false;
resultName = dataName+".b64.txt";
} else {
resultString = new String(result, CommonEncrypt.ENCRYPT_STANDARD_CHARSET);
}
}
case "base64decode", "base64d", "b64d", "base64url-decode", "base64ud", "b64ud" -> {
final Base64.Decoder b64tool = command.getArgs()[0].contains("u") ? Base64.getUrlDecoder() : Base64.getDecoder();
try { result = b64tool.decode(data); }
catch (IllegalArgumentException e) {
MornyCoeur.extra().exec(new SendSticker(
event.message().chat().id(), TelegramStickers.ID_404
).replyToMessageId(event.message().messageId()));
return;
}
if (!inputText) {
echoString = false;
resultName = CommonEncrypt.base64FilenameLint(dataName);
} else {
resultString = new String(result, CommonEncrypt.ENCRYPT_STANDARD_CHARSET);
}
}
case "md5" -> resultString = CommonConvert.byteArrayToHex(CommonEncrypt.hashMd5(data));
case "sha1" -> resultString = CommonConvert.byteArrayToHex(CommonEncrypt.hashSha1(data));
case "sha256" -> resultString = CommonConvert.byteArrayToHex(CommonEncrypt.hashSha256(data));
case "sha512" -> resultString = CommonConvert.byteArrayToHex(CommonEncrypt.hashSha512(data));
default -> {
MornyCoeur.extra().exec(new SendSticker(
event.message().chat().id(), TelegramStickers.ID_404
).replyToMessageId(event.message().messageId()));
return;
}
}
if (modUpperCase) {
// modUpperCase support only algorithm that showed as HEX value.
// it means md5, sha1, sha256, sha512 here.
// other will report wrong param.
switch (command.getArgs()[0]) {
case "md5", "sha1", "sha256", "sha512" -> {
assert resultString != null;
resultString = resultString.toUpperCase();
}
default -> {
MornyCoeur.extra().exec(new SendSticker(
event.message().chat().id(), TelegramStickers.ID_404
).replyToMessageId(event.message().messageId()));
return;
}
}
}
if (echoString) {
MornyCoeur.extra().exec(new SendMessage(
event.message().chat().id(),
"<pre><code>" + MsgEscape.escapeHtml(resultString) + "</code></pre>"
).replyToMessageId(event.message().messageId()).parseMode(ParseMode.HTML));
} else {
MornyCoeur.extra().exec(new SendDocument(
event.message().chat().id(),
result
).fileName(resultName).replyToMessageId(event.message().messageId()));
}
}
}

View File

@ -1,99 +0,0 @@
package cc.sukazyo.cono.morny.bot.command;
import cc.sukazyo.cono.morny.MornyCoeur;
import cc.sukazyo.cono.morny.MornyTrusted;
import cc.sukazyo.cono.morny.bot.event.OnEventHackHandle;
import cc.sukazyo.cono.morny.data.TelegramStickers;
import cc.sukazyo.cono.morny.util.tgapi.InputCommand;
import com.pengrad.telegrambot.model.Update;
import com.pengrad.telegrambot.request.SendSticker;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* {@link OnEventHackHandle} 的命令行前端
* @since 0.4.2.0
*/
public class EventHack implements ITelegramCommand {
@Nonnull @Override public String getName () { return "event_hack"; }
@Nullable @Override public String[] getAliases () { return null; }
@Nonnull @Override public String getParamRule () { return "[(user|group|any)]"; }
@Nonnull @Override public String getDescription () { return "输出 bot 下一个获取到的事件序列化数据"; }
/**
* {@link OnEventHackHandle} 的命令行前端<br>
* <br>
* 实现了通过命令行进行 EventHack 功能<br>
* 支持三种模式默认为 {@link OnEventHackHandle.HackType#USER USER}
* {@link OnEventHackHandle.HackType#ANY ANY} 将会通过 {@link MornyTrusted#isTrusted(long)} 检查触发用户的权限
*
* @param event 命令基础参数触发的事件对象本身
* @param command 命令基础参数解析出的命令对象
* @since 0.4.2.0
*/
@Override
public void execute (@Nonnull InputCommand command, @Nonnull Update event) {
enum Status {
OK,
FORBIDDEN_FOR_ANY
}
Status status;
String x_mode = "";
if (command.hasArgs()) {
x_mode = command.getArgs()[0];
}
switch (x_mode) {
case "any" -> {
if (MornyCoeur.trustedInstance().isTrusted(event.message().from().id())) {
OnEventHackHandle.registerHack(
event.message().messageId(),
event.message().from().id(),
event.message().chat().id(),
OnEventHackHandle.HackType.ANY
);
status = Status.OK;
} else {
status = Status.FORBIDDEN_FOR_ANY;
}
}
case "group" -> {
OnEventHackHandle.registerHack(
event.message().messageId(),
event.message().from().id(),
event.message().chat().id(),
OnEventHackHandle.HackType.GROUP
);
status = Status.OK;
}
default -> {
OnEventHackHandle.registerHack(
event.message().messageId(),
event.message().from().id(),
event.message().chat().id(),
OnEventHackHandle.HackType.USER
);
status = Status.OK;
}
}
switch (status) {
case OK -> MornyCoeur.extra().exec(new SendSticker(
event.message().chat().id(),
TelegramStickers.ID_WAITING
).replyToMessageId(event.message().messageId())
);
case FORBIDDEN_FOR_ANY -> MornyCoeur.extra().exec(new SendSticker(
event.message().chat().id(),
TelegramStickers.ID_403
).replyToMessageId(event.message().messageId())
);
}
}
}

View File

@ -1,85 +0,0 @@
package cc.sukazyo.cono.morny.bot.command;
import cc.sukazyo.cono.morny.MornyCoeur;
import cc.sukazyo.cono.morny.util.tgapi.InputCommand;
import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramUserInformation;
import com.pengrad.telegrambot.model.Update;
import com.pengrad.telegrambot.model.User;
import com.pengrad.telegrambot.model.request.ParseMode;
import com.pengrad.telegrambot.request.GetChatMember;
import com.pengrad.telegrambot.request.SendMessage;
import com.pengrad.telegrambot.response.GetChatMemberResponse;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class GetUsernameAndId implements ITelegramCommand {
@Nonnull @Override public String getName () { return "user"; }
@Nullable @Override public String[] getAliases () { return null; }
@Nonnull @Override public String getParamRule () { return "[userid]"; }
@Nonnull @Override public String getDescription () { return "获取指定或回复的用户相关信息"; }
@Override
public void execute (@Nonnull InputCommand command, @Nonnull Update event) {
final String[] args = command.getArgs();
// 不支持大于一个参数
if (args.length > 1) { MornyCoeur.extra().exec(new SendMessage(
event.message().chat().id(),
"[Unavailable] Too much arguments."
).replyToMessageId(event.message().messageId())); return; }
// 发送者自己的 id
long userId = event.message().from().id();
// 如果有回复某个人则使用被回复人的 id
if (event.message().replyToMessage()!= null) {
userId = event.message().replyToMessage().from().id();
}
// 如果有指定 id则使用指定的 id
if (args.length > 0) {
try {
userId = Long.parseLong(args[0]);
} catch (NumberFormatException e) {
MornyCoeur.extra().exec(new SendMessage(
event.message().chat().id(),
"[Unavailable] " + e.getMessage()
).replyToMessageId(event.message().messageId()));
return;
}
}
// 重新获取用户对象
final GetChatMemberResponse response = MornyCoeur.getAccount().execute(
new GetChatMember(event.message().chat().id(), userId)
);
if (response.chatMember() == null) {
MornyCoeur.extra().exec(new SendMessage(
event.message().chat().id(),
"[Unavailable] user not found."
).replyToMessageId(event.message().messageId()));
return;
}
// 获取并发送用户信息
final User user = response.chatMember().user();
if (user.id() == 136817688) {
MornyCoeur.extra().exec(new SendMessage(
event.message().chat().id(),
"<code>$__channel_identify</code>"
));
return;
}
MornyCoeur.extra().exec(new SendMessage(
event.message().chat().id(),
TelegramUserInformation.informationOutputHTML(user)
).replyToMessageId(event.message().messageId()).parseMode(ParseMode.HTML));
}
}

View File

@ -1,85 +0,0 @@
package cc.sukazyo.cono.morny.bot.command;
import cc.sukazyo.cono.morny.MornyCoeur;
import cc.sukazyo.cono.morny.data.ip186.IP186QueryResponse;
import cc.sukazyo.cono.morny.data.ip186.IP186QueryHandler;
import cc.sukazyo.cono.morny.util.tgapi.InputCommand;
import com.pengrad.telegrambot.model.Update;
import com.pengrad.telegrambot.model.request.ParseMode;
import com.pengrad.telegrambot.request.SendMessage;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import static cc.sukazyo.cono.morny.util.tgapi.formatting.MsgEscape.escapeHtml;
/**
* {@value IP186QueryHandler#SITE_URL} 查询的 telegram 命令前端
* @since 0.4.2.10
*/
public class Ip186Query {
public static final String CMD_IP = "ip";
public static final String CMD_WHOIS = "whois";
public static class Ip implements ITelegramCommand {
@Nonnull @Override public String getName () { return CMD_IP; }
@Nullable @Override public String[] getAliases () { return new String[0]; }
@Nonnull @Override public String getParamRule () { return "[ip]"; }
@Nonnull @Override public String getDescription () { return "通过 https://ip.186526.xyz 查询 ip 资料"; }
@Override public void execute (@Nonnull InputCommand command, @Nonnull Update event) { exec(event, command); }
}
public static class Whois implements ITelegramCommand {
@Nonnull @Override public String getName () { return CMD_WHOIS; }
@Nullable @Override public String[] getAliases () { return new String[0]; }
@Nonnull @Override public String getParamRule () { return "[domain]"; }
@Nonnull @Override public String getDescription () { return "通过 https://ip.186526.xyz 查询域名资料"; }
@Override public void execute (@Nonnull InputCommand command, @Nonnull Update event) { exec(event, command); }
}
private static void exec (@Nonnull Update event, @Nonnull InputCommand command) {
String arg = null;
if (!command.hasArgs()) {
if (event.message().replyToMessage() != null) {
arg = event.message().replyToMessage().text();
}
} else if (command.getArgs().length > 1) {
MornyCoeur.extra().exec(new SendMessage(
event.message().chat().id(),
"[Unavailable] Too much arguments."
).replyToMessageId(event.message().messageId()));
return;
} else {
arg = command.getArgs()[0];
}
if (arg == null) {
MornyCoeur.extra().exec(new SendMessage(
event.message().chat().id(),
"[Unavailable] No ip defined."
).replyToMessageId(event.message().messageId()));
return;
}
try {
IP186QueryResponse response = switch (command.getCommand()) {
case CMD_IP -> IP186QueryHandler.queryIp(arg);
case CMD_WHOIS -> IP186QueryHandler.queryWhoisPretty(arg);
default -> throw new IllegalArgumentException("Unknown 186-IP query method " + command.getCommand());
};
MornyCoeur.extra().exec(new SendMessage(
event.message().chat().id(),
escapeHtml(response.url()) + "\n<code>" + escapeHtml(response.body()) + "</code>"
).parseMode(ParseMode.HTML).replyToMessageId(event.message().messageId()));
} catch (Exception e) {
MornyCoeur.extra().exec(new SendMessage(
event.message().chat().id(),
"[Exception] in query:\n<code>" + escapeHtml(e.getMessage()) + "</code>"
).parseMode(ParseMode.HTML).replyToMessageId(event.message().messageId()));
}
}
}

View File

@ -1,44 +0,0 @@
package cc.sukazyo.cono.morny.bot.command;
import com.pengrad.telegrambot.model.Update;
import com.pengrad.telegrambot.model.request.ParseMode;
import com.pengrad.telegrambot.request.SendPhoto;
import cc.sukazyo.cono.morny.MornyCoeur;
import cc.sukazyo.cono.morny.util.tgapi.InputCommand;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* The implementation of Telegram special command `/start`.
*
* @see MornyInformation related class where some data comes from.
*
* @since 1.0.0-RC4
*/
public class MornyInfoOnHello implements ISimpleCommand {
@Nonnull @Override public String getName() { return "start"; }
@Nullable @Override public String[] getAliases() { return new String[0]; }
// @Override public String getParamRule() { return ""; }
// @Override public String getDescription() { return "" }
@Override
public void execute(@Nonnull InputCommand command, @Nonnull Update event) {
MornyCoeur.extra().exec(new SendPhoto(
event.message().chat().id(),
MornyInformation.getAboutPic()
).caption("""
欢迎使用 <b>Morny Cono</b><i>来自安妮的侍从小鼠</i>
Morny 具有各种各样的功能
%s
你可以随时通过 /info 重新获得这些信息""".formatted(MornyInformation.getMornyAboutLinksHTML())
).parseMode(ParseMode.HTML));
}
}

View File

@ -1,301 +0,0 @@
package cc.sukazyo.cono.morny.bot.command;
import cc.sukazyo.cono.morny.BuildConfig;
import cc.sukazyo.cono.morny.MornyAbout;
import cc.sukazyo.cono.morny.MornyCoeur;
import cc.sukazyo.cono.morny.MornySystem;
import cc.sukazyo.cono.morny.data.TelegramImages;
import cc.sukazyo.cono.morny.data.TelegramStickers;
import cc.sukazyo.cono.morny.util.tgapi.ExtraAction;
import cc.sukazyo.cono.morny.util.tgapi.InputCommand;
import com.pengrad.telegrambot.model.Update;
import com.pengrad.telegrambot.model.request.ParseMode;
import com.pengrad.telegrambot.request.SendMessage;
import com.pengrad.telegrambot.request.SendPhoto;
import com.pengrad.telegrambot.request.SendSticker;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Objects;
import static cc.sukazyo.cono.morny.util.CommonFormat.formatDate;
import static cc.sukazyo.cono.morny.util.CommonFormat.formatDuration;
import static cc.sukazyo.cono.morny.util.tgapi.formatting.MsgEscape.escapeHtml;
public class MornyInformation implements ITelegramCommand {
private static final String SUB_STICKER = "stickers";
private static final String SUB_RUNTIME = "runtime";
private static final String SUB_VERSION = "version";
private static final String SUB_VERSION_2 = "v";
@Nonnull @Override public String getName () { return "info"; }
@Nullable @Override public String[] getAliases () { return new String[0]; }
@Nonnull @Override public String getParamRule () { return "[(version|runtime|stickers[.IDs])]"; }
@Nonnull @Override public String getDescription () { return "输出当前 Morny 的各种信息"; }
@Override
public void execute (@Nonnull InputCommand command, @Nonnull Update event) {
if (!command.hasArgs()) {
echoInfo(event.message().chat().id(), event.message().messageId());
return;
}
final String action = command.getArgs()[0];
if (action.startsWith(SUB_STICKER)) {
echoStickers(command, event);
} else if (action.equals(SUB_RUNTIME)) {
echoRuntime(event);
} else if (action.equals(SUB_VERSION) || action.equals(SUB_VERSION_2)) {
echoVersion(event);
} else {
echo404(event);
}
}
/**
* Subcommand <u>/info</u> without params.
*
* @since 1.0.0-RC4
*/
public void echoInfo (long chatId, int replayToMessage) {
MornyCoeur.extra().exec(new SendPhoto(
chatId,
getAboutPic()
).caption("""
<b>Morny Cono</b>
来自安妮的侍从小鼠
%s""".formatted(getMornyAboutLinksHTML())
).parseMode(ParseMode.HTML).replyToMessageId(replayToMessage));
}
/**
* subcommand <u>/info stickers</u>
*
* @see #SUB_STICKER
*/
public void echoStickers (@Nonnull InputCommand command, @Nonnull Update event) {
final long echoTo = event.message().chat().id();
final int replyToMessage = event.message().messageId();
String id = null;
if (command.getArgs()[0].equals(SUB_STICKER)) {
if (command.getArgs().length == 1) {
id = "";
} else if (command.getArgs().length == 2) {
id = command.getArgs()[1];
}
} else if (command.getArgs().length == 1) {
if (command.getArgs()[0].startsWith(SUB_STICKER+".") || command.getArgs()[0].startsWith(SUB_STICKER+"#")) {
id = command.getArgs()[0].substring(SUB_STICKER.length()+1);
}
}
if (id == null) { echo404(event); return; }
echoStickers(id, echoTo, replyToMessage);
}
/**
* telegram 输出一个或全部 sticker.
*
* @param id
* sticker {@link TelegramStickers} 中的字段名
* 使用 {@link ""}(空字符串)(不是{@link null}) 表示输出全部 sticker
* @param chatId 目标 chat id
* @param messageId 要回复的消息 id依据 {@link TelegramStickers#echoStickerByID(String, ExtraAction, long, int) 上游}
* 逻辑使用 {@link -1} 表示不回复消息
*
* @see TelegramStickers#echoStickerByID(String, ExtraAction, long, int)
* @see TelegramStickers#echoAllStickers(ExtraAction, long, int)
*/
public static void echoStickers (@Nonnull String id, long chatId, int messageId) {
if (id.isEmpty()) TelegramStickers.echoAllStickers(MornyCoeur.extra(), chatId, messageId);
else TelegramStickers.echoStickerByID(id, MornyCoeur.extra(), chatId, messageId);
}
/**
* Subcommand <u>/info runtime</u>.
*
* @see #SUB_RUNTIME
*
* @since 1.0.0-alpha4
*/
public static void echoRuntime (@Nonnull Update event) {
MornyCoeur.extra().exec(new SendMessage(
event.message().chat().id(),
String.format("""
system:
- <code>%s</code>
- <code>%s</code> (<code>%s</code>) <code>%s</code>
java runtime:
- <code>%s</code>
- <code>%s</code>
vm memory:
- <code>%d</code> / <code>%d</code> MB
- <code>%d</code> cores
coeur version:
- %s
- <code>%s</code>
- <code>%s [UTC]</code>
- [<code>%d</code>]
continuous:
- <code>%s</code>
- [<code>%d</code>]
- <code>%s [UTC]</code>
- [<code>%d</code>]""",
// system
escapeHtml(getRuntimeHostName()==null ? "<unknown-host>" : getRuntimeHostName()),
escapeHtml(System.getProperty("os.name")),
escapeHtml(System.getProperty("os.arch")),
escapeHtml(System.getProperty("os.version")),
// java
escapeHtml(System.getProperty("java.vm.vendor")+"."+System.getProperty("java.vm.name")),
escapeHtml(System.getProperty("java.vm.version")),
// memory
Runtime.getRuntime().totalMemory() / 1024 / 1024,
Runtime.getRuntime().maxMemory() / 1024 / 1024,
Runtime.getRuntime().availableProcessors(),
// version
getVersionAllFullTagHtml(),
escapeHtml(MornySystem.getJarMd5()),
escapeHtml(formatDate(BuildConfig.CODE_TIMESTAMP, 0)),
BuildConfig.CODE_TIMESTAMP,
// continuous
escapeHtml(formatDuration(System.currentTimeMillis() - MornyCoeur.coeurStartTimestamp)),
System.currentTimeMillis() - MornyCoeur.coeurStartTimestamp,
escapeHtml(formatDate(MornyCoeur.coeurStartTimestamp, 0)),
MornyCoeur.coeurStartTimestamp
)
).replyToMessageId(event.message().messageId()).parseMode(ParseMode.HTML));
}
/**
* Subcommand <u>/info version</u> or <u>/info v</u>.
*
* @see #SUB_VERSION
* @see #SUB_VERSION_2
*
* @since 1.0.0-alpha4
*/
public static void echoVersion (@Nonnull Update event) {
MornyCoeur.extra().exec(new SendMessage(
event.message().chat().id(),
String.format(
"""
version:
- Morny <code>%s</code>
- <code>%s</code>%s%s
core md5_hash:
- <code>%s</code>
coding timestamp:
- <code>%d</code>
- <code>%s [UTC]</code>""",
escapeHtml(MornySystem.CODENAME.toUpperCase()),
escapeHtml(MornySystem.VERSION_BASE),
MornySystem.isUseDelta() ? String.format("-δ<code>%s</code>", escapeHtml(Objects.requireNonNull(MornySystem.VERSION_DELTA))) : "",
MornySystem.isGitBuild() ? "\n- git "+getVersionGitTagHtml() : "",
escapeHtml(MornySystem.getJarMd5()),
BuildConfig.CODE_TIMESTAMP,
escapeHtml(formatDate(BuildConfig.CODE_TIMESTAMP, 0))
)
).replyToMessageId(event.message().messageId()).parseMode(ParseMode.HTML));
}
/**
* 取得 {@link MornySystem} git commit 相关版本信息的 HTML 格式化标签.
*
* @return 格式类似于 <u>{@code 28e8c82a.δ}</u> 的以 HTML 方式格式化的版本号组件
* 其中 {@code .δ} 对应着 {@link MornySystem#isCleanBuild}
* commit tag 字段如果支援 {@link MornySystem#currentCodePath} 则会以链接形式解析否则则为 code 格式
* <small>为了对 telegram api html 格式兼容所以不支援嵌套链接与code标签</small>
* 如果 {@link MornySystem#isGitBuild} {@link false}则方法会返回 {@link ""}
*
* @since 1.0.0-beta2
*/
@Nonnull
public static String getVersionGitTagHtml () {
if (!MornySystem.isGitBuild()) return "";
final StringBuilder g = new StringBuilder();
final String cp = MornySystem.currentCodePath();
if (cp == null) g.append(String.format("<code>%s</code>", BuildConfig.COMMIT.substring(0, 8)));
else g.append(String.format("<a href='%s'>%s</a>", MornySystem.currentCodePath(), BuildConfig.COMMIT.substring(0, 8)));
if (!MornySystem.isCleanBuild()) g.append(".<code>δ</code>");
return g.toString();
}
/**
* 取得完整 Morny 版本的 HTML 格式化标签.
* <p>
* 相比于 {@link MornySystem#VERSION_FULL}这个版本号还包含了 {@link MornySystem#CODENAME 版本 codename}
* 各个部分也被以 HTML 的格式进行了格式化以可以更好的在富文本中插入使用.
* @return 基于 HTML 标签进行了格式化了的类似于
* <u><code>{@link MornySystem#VERSION_BASE 5.38.2-alpha1}{@link MornySystem#isUseDelta() -δ}{@link MornySystem#VERSION_DELTA tt}{@link MornySystem#isGitBuild() +git.}{@link #getVersionGitTagHtml() 28e8c82a.δ}*{@link MornySystem#CODENAME TOKYO}</code></u>
* 的版本号
* @since 1.0.0-beta2
*/
@Nonnull
public static String getVersionAllFullTagHtml () {
final StringBuilder v = new StringBuilder();
v.append("<code>").append(MornySystem.VERSION_BASE).append("</code>");
if (MornySystem.isUseDelta()) v.append("-δ<code>").append(MornySystem.VERSION_DELTA).append("</code>");
if (MornySystem.isGitBuild()) v.append("+git.").append(getVersionGitTagHtml());
v.append("*<code>").append(MornySystem.CODENAME.toUpperCase()).append("</code>");
return v.toString();
}
/**
* 获取 coeur 运行时的宿主机的主机名
* @return coeur 宿主机主机名或者 {@link null} 表示获取失败
*/
@Nullable
public static String getRuntimeHostName () {
try {
return InetAddress.getLocalHost().getHostName();
} catch (UnknownHostException e) {
return null;
}
}
/**
* Get the about-pic (intro picture or featured image) of Morny.
*
* @return the Telegram file binary data of the about-pic.
* @throws IllegalStateException {@link TelegramImages.AssetsFileImage#get() get() image data} may
* throws {@link IllegalStateException} while read error.
*/
@Nonnull
public static byte[] getAboutPic () {
return TelegramImages.IMG_ABOUT.get();
}
private static void echo404 (@Nonnull Update event) {
MornyCoeur.extra().exec(new SendSticker(
event.message().chat().id(),
TelegramStickers.ID_404
).replyToMessageId(event.message().messageId()));
}
/**
* The formatted about links of Morny Cono and Morny Coeur.
* <p>
* With the Telegram HTML formatting, used in <u>/info</u> and <u>/start</u>.
* Provided the end user the links that can find resources about Morny.
*/
@Nonnull
public static String getMornyAboutLinksHTML () {
return """
<a href='%s'>source code</a> | <a href='%s'>backup</a>
<a href='%s'>反馈 / issue tracker</a>
<a href='%s'>使用说明书 / user guide & docs</a>""".formatted(
MornyAbout.MORNY_SOURCECODE_LINK, MornyAbout.MORNY_SOURCECODE_SELF_HOSTED_MIRROR_LINK,
MornyAbout.MORNY_ISSUE_TRACKER_LINK,
MornyAbout.MORNY_USER_GUIDE_LINK
);
}
}

View File

@ -1,80 +0,0 @@
package cc.sukazyo.cono.morny.bot.command;
import cc.sukazyo.cono.morny.data.TelegramStickers;
import cc.sukazyo.cono.morny.util.tgapi.InputCommand;
import com.pengrad.telegrambot.model.Update;
import com.pengrad.telegrambot.model.request.ParseMode;
import com.pengrad.telegrambot.request.SendMessage;
import cc.sukazyo.cono.morny.MornyCoeur;
import cc.sukazyo.cono.morny.data.NbnhhshQuery;
import com.pengrad.telegrambot.request.SendSticker;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import static cc.sukazyo.cono.morny.util.CommonConvert.stringsConnecting;
import static cc.sukazyo.cono.morny.util.tgapi.formatting.MsgEscape.escapeHtml;
public class Nbnhhsh implements ITelegramCommand {
@Nonnull @Override public String getName () { return "nbnhhsh"; }
@Nullable @Override public String[] getAliases () { return null; }
@Nonnull @Override public String getParamRule () { return "[text]"; }
@Nonnull @Override public String getDescription () { return "检索文本内 nbnhhsh 词条"; }
@Override
public void execute (@Nonnull InputCommand command, @Nonnull Update event) {
class TagNoContent extends Exception {}
try {
String queryTarget;
if (event.message().replyToMessage() != null && event.message().replyToMessage().text() != null)
queryTarget = event.message().replyToMessage().text();
else if (command.hasArgs())
queryTarget = stringsConnecting(command.getArgs(), " ", 0, command.getArgs().length-1);
else {
throw new TagNoContent();
}
NbnhhshQuery.GuessResult response = NbnhhshQuery.sendGuess(queryTarget);
StringBuilder message = new StringBuilder("<a href=\"https://lab.magiconch.com/nbnhhsh/\">## Result of nbnhhsh query :</a>");
for (NbnhhshQuery.Word word : response.words) {
if (word.trans != null && word.trans.length == 0) word.trans = null;
if (word.inputting != null && word.inputting.length == 0) word.inputting = null;
if (word.trans == null && word.inputting == null) continue;
message.append("\n\n<b>[[ ").append(escapeHtml(word.name)).append(" ]]</b>");
if (word.trans != null) for (String trans : word.trans) {
message.append("\n* <i>").append(escapeHtml(trans)).append("</i>");
}
if (word.inputting != null) {
if (word.trans != null) message.append("\n");
message.append(" maybe:");
for (String trans : word.inputting) {
message.append("\n` <i>").append(escapeHtml(trans)).append("</i>");
}
}
}
MornyCoeur.extra().exec(new SendMessage(
event.message().chat().id(),
message.toString()
).parseMode(ParseMode.HTML).replyToMessageId(event.message().messageId()));
} catch (TagNoContent tag) {
MornyCoeur.extra().exec(new SendSticker(
event.message().chat().id(), TelegramStickers.ID_404
).replyToMessageId(event.message().messageId()));
} catch (Exception e) {
MornyCoeur.extra().exec(new SendMessage(
event.message().chat().id(),
"[Exception] in query:\n<code>" + escapeHtml(e.getMessage()) + "</code>"
).parseMode(ParseMode.HTML).replyToMessageId(event.message().messageId()));
}
}
}

View File

@ -1,36 +0,0 @@
package cc.sukazyo.cono.morny.bot.command;
import cc.sukazyo.cono.morny.MornyCoeur;
import cc.sukazyo.cono.morny.util.tgapi.InputCommand;
import com.pengrad.telegrambot.model.Update;
import com.pengrad.telegrambot.model.request.ParseMode;
import com.pengrad.telegrambot.request.SendMessage;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class Testing implements ISimpleCommand {
@Nonnull
@Override
public String getName () {
return "test";
}
@Nullable
@Override
public String[] getAliases () {
return null;
}
@Override
public void execute (@Nonnull InputCommand command, @Nonnull Update event) {
MornyCoeur.extra().exec(new SendMessage(
event.message().chat().id(),
"<b>Just</b> a TEST command."
).replyToMessageId(event.message().messageId()).parseMode(ParseMode.HTML));
}
}

View File

@ -1,82 +0,0 @@
package cc.sukazyo.cono.morny.bot.command;
import cc.sukazyo.cono.morny.MornyCoeur;
import cc.sukazyo.cono.morny.data.TelegramStickers;
import cc.sukazyo.cono.morny.util.tgapi.InputCommand;
import com.pengrad.telegrambot.model.Message;
import com.pengrad.telegrambot.model.Update;
import com.pengrad.telegrambot.model.request.ParseMode;
import com.pengrad.telegrambot.request.SendMessage;
import com.pengrad.telegrambot.request.SendSticker;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* <b>WARNING</b> that {@link cc.sukazyo.cono.morny.bot.event.OnTelegramCommand}
* 并不能够处理非 english word 字符之外的命令.
* <p>
* 出于这个限制以下几个命令目前都无法使用
* @see 抱抱
* @see 揉揉
* @see 蹭蹭
* @see 贴贴
*/
@SuppressWarnings("NonAsciiCharacters")
public class 喵呜 {
public static class 抱抱 implements ISimpleCommand {
@Nonnull @Override public String getName () { return "抱抱"; }
@Nullable @Override public String[] getAliases () { return new String[0]; }
@Override public void execute (@Nonnull InputCommand command, @Nonnull Update event) {
replyingSet(event, "抱抱", "抱抱");
}
}
public static class 揉揉 implements ISimpleCommand {
@Nonnull @Override public String getName () { return "揉揉"; }
@Nullable @Override public String[] getAliases () { return new String[0]; }
@Override public void execute (@Nonnull InputCommand command, @Nonnull Update event) {
replyingSet(event, "蹭蹭", "摸摸");
}
}
public static class 蹭蹭 implements ISimpleCommand {
@Nonnull @Override public String getName () { return "蹭蹭"; }
@Nullable @Override public String[] getAliases () { return new String[0]; }
@Override public void execute (@Nonnull InputCommand command, @Nonnull Update event) {
replyingSet(event, "揉揉", "蹭蹭");
}
}
public static class 贴贴 implements ISimpleCommand {
@Nonnull @Override public String getName () { return "贴贴"; }
@Nullable @Override public String[] getAliases () { return new String[0]; }
@Override public void execute (@Nonnull InputCommand command, @Nonnull Update event) {
replyingSet(event, "贴贴", "贴贴");
}
}
private static void replyingSet (@Nonnull Update event, @Nonnull String whileRec, @Nonnull String whileNew) {
final boolean isNew = event.message().replyToMessage() == null;
final Message target = isNew ? event.message() : event.message().replyToMessage();
MornyCoeur.extra().exec(new SendMessage(
event.message().chat().id(),
isNew ? whileNew : whileRec
).replyToMessageId(target.messageId()).parseMode(ParseMode.HTML));
}
public static class Progynova implements ITelegramCommand {
@Nonnull @Override public String getName () { return "install"; }
@Nullable @Override public String[] getAliases () { return new String[0]; }
@Nonnull @Override public String getParamRule () { return ""; }
@Nonnull @Override public String getDescription () { return "抽取一个神秘盒子"; }
@Override public void execute (@Nonnull InputCommand command, @Nonnull Update event) {
MornyCoeur.extra().exec(new SendSticker(
event.message().chat().id(),
TelegramStickers.ID_PROGYNOVA
).replyToMessageId(event.message().messageId()));
}
}
}

View File

@ -1,40 +0,0 @@
package cc.sukazyo.cono.morny.bot.command;
import cc.sukazyo.cono.morny.MornyCoeur;
import cc.sukazyo.cono.morny.util.tgapi.InputCommand;
import com.pengrad.telegrambot.model.Update;
import com.pengrad.telegrambot.request.SendMessage;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import static cc.sukazyo.cono.morny.util.CommonRandom.probabilityTrue;
@SuppressWarnings("NonAsciiCharacters")
public class 私わね implements ISimpleCommand {
@Nonnull
@Override public String getName () { return "me"; }
@Nullable
@Override public String[] getAliases () { return null; }
@Override
public void execute (@Nonnull InputCommand command, @Nonnull Update event) {
if (probabilityTrue(521)) {
// 可以接入未来的心情系统如果有的话
// final String text = switch (ThreadLocalRandom.current().nextInt(11)) {
// case 0,7,8,9,10 -> "才不是";
// case 1,2,3,6 -> "才不是!";
// case 4,5 -> "才不是..";
// default -> throw new IllegalStateException("Unexpected random value in 私わね command.");
// };
final String text = "/打假";
MornyCoeur.extra().exec(new SendMessage(
event.message().chat().id(),
text
).replyToMessageId(event.message().messageId()));
}
}
}

View File

@ -1,181 +0,0 @@
package cc.sukazyo.cono.morny.bot.event;
import cc.sukazyo.cono.morny.MornyCoeur;
import cc.sukazyo.cono.morny.bot.api.EventListener;
import cc.sukazyo.cono.morny.data.TelegramStickers;
import cc.sukazyo.cono.morny.util.CommonFormat;
import cc.sukazyo.cono.morny.util.tgapi.formatting.MsgEscape;
import cc.sukazyo.cono.morny.util.tgapi.formatting.TGToString;
import com.pengrad.telegrambot.model.Chat;
import com.pengrad.telegrambot.model.Message;
import com.pengrad.telegrambot.model.Update;
import com.pengrad.telegrambot.model.request.ParseMode;
import com.pengrad.telegrambot.request.ForwardMessage;
import com.pengrad.telegrambot.request.GetChat;
import com.pengrad.telegrambot.request.SendMessage;
import com.pengrad.telegrambot.request.SendSticker;
import com.pengrad.telegrambot.response.SendResponse;
import javax.annotation.Nonnull;
/**
* 通过 bot 呼叫主人的事件监听管理类
* @since 0.4.2.1
*/
public class OnCallMe extends EventListener {
/**
* 主人的 telegram user id同时被用于 chat id<br>
* 跟随 {@link cc.sukazyo.cono.morny.MornyConfig#trustedMaster} 的值
* @since 0.4.2.1
*/
private static final long ME = MornyCoeur.config().trustedMaster;
/**
* 监听私聊 bot 的消息进行呼叫关键字匹配
* 如果成功将会执行呼叫函数并向呼叫者回显{@link TelegramStickers#ID_WAITING "已呼叫"贴纸}
*
* @param update 事件基础参数消息事件所属的 tgapi:update 对象
* @return 事件基础返回值是否已完成处理事件<br>
* 如果匹配到呼叫则返回{@code true}反之返回{@code false}
*/
@Override
public boolean onMessage (@Nonnull Update update) {
if (update.message().text() == null)
return false;
if (update.message().chat().type() != Chat.Type.Private)
return false;
switch (update.message().text().toLowerCase()) {
case "steam", "sbeam", "sdeam" ->
requestSteamJoin(update);
case "hana paresu", "花宫", "内群" ->
requestHanaParesuJoin(update);
case "dinner", "lunch", "breakfast", "meal", "eating", "安妮今天吃什么" ->
requestLastDinner(update);
default -> {
if (update.message().text().startsWith("cc::")) {
requestCustomCall(update);
break;
}
return false;
}
}
MornyCoeur.extra().exec(new SendSticker(
update.message().chat().id(),
TelegramStickers.ID_SENT
).replyToMessageId(update.message().messageId())
);
return true;
}
/**
* 执行 steam library 呼叫<br>
* 将会向 {@link #ME} 发送
*
* @param event 执行呼叫的tg事件
*/
private static void requestSteamJoin (Update event) {
MornyCoeur.extra().exec(new SendMessage(
ME, String.format(
"""
request <b>STEAM LIBRARY</b>
from %s""",
TGToString.as(event.message().from()).fullnameRefHtml()
)
).parseMode(ParseMode.HTML));
}
/**
* 执行花宫呼叫<br>
* 将会向 {@link #ME} 发送
*
* @param event 执行呼叫的tg事件
*/
private static void requestHanaParesuJoin (Update event) {
MornyCoeur.extra().exec(new SendMessage(
ME, String.format(
"""
request <b>Hana Paresu</b>
from %s""",
TGToString.as(event.message().from()).fullnameRefHtml()
)
).parseMode(ParseMode.HTML));
}
/**
* 对访问最近一次的饭局的请求进行回复<br>
*
* @param event 执行呼叫的tg事件
*/
private static void requestLastDinner (Update event) {
boolean isAllowed = false;
Message lastDinnerData = null;
if (MornyCoeur.trustedInstance().isTrustedForDinnerRead(event.message().from().id())) {
lastDinnerData = MornyCoeur.extra().exec(new GetChat(MornyCoeur.config().dinnerChatId)).chat().pinnedMessage();
SendResponse sendResp = MornyCoeur.extra().exec(new ForwardMessage(
event.message().from().id(),
lastDinnerData.forwardFromChat().id(),
lastDinnerData.forwardFromMessageId()
));
MornyCoeur.extra().exec(new SendMessage(
event.message().from().id(),
String.format("<i>on</i> <code>%s [UTC+8]</code>\n- <code>%s</code> <i>before</i>",
MsgEscape.escapeHtml(
CommonFormat.formatDate((long)lastDinnerData.forwardDate()*1000, 8)
), MsgEscape.escapeHtml(
CommonFormat.formatDuration(System.currentTimeMillis()-(long)lastDinnerData.forwardDate()*1000)
)
)
).replyToMessageId(sendResp.message().messageId()).parseMode(ParseMode.HTML));
isAllowed = true;
} else {
MornyCoeur.extra().exec(new SendSticker(
event.message().from().id(),
TelegramStickers.ID_403
).replyToMessageId(event.message().messageId()));
}
MornyCoeur.extra().exec(new SendMessage(
ME, String.format(
"""
request <b>Last Annie Dinner</b>
from %s
%s""",
TGToString.as(event.message().from()).fullnameRefHtml(),
isAllowed ? "Allowed and returned " + String.format(
"https://t.me/c/%d/%d", Math.abs(lastDinnerData.forwardFromChat().id()+1000000000000L), lastDinnerData.forwardFromMessageId()
) : "Forbidden by perm check."
)
).parseMode(ParseMode.HTML));
}
/**
* 执行自定义呼叫<br>
* 将会向 {@link #ME} 发送一个 request 数据消息和转发的原始请求消息<br>
* <br>
* <u>known issue</u><ul>
* <li>无法处理与转发带有媒体的消息</li>
* </ul>
* <br>
* 现在你可以通过这个 bot 来呼叫主人sukazyo任何事情了
* <s>但是直接私聊sukazyo不好吗</s>
*
* @param event 执行呼叫的tg事件
* @since 0.4.2.2
*/
private static void requestCustomCall (Update event) {
MornyCoeur.extra().exec(new SendMessage(
ME, String.format(
"""
request <u>[???]</u>
from %s""",
TGToString.as(event.message().from()).fullnameRefHtml()
)
).parseMode(ParseMode.HTML));
MornyCoeur.extra().exec(new ForwardMessage(
ME,
event.message().chat().id(),
event.message().messageId()
));
}
}

View File

@ -1,47 +0,0 @@
package cc.sukazyo.cono.morny.bot.event;
import cc.sukazyo.cono.morny.MornyCoeur;
import cc.sukazyo.cono.morny.bot.api.EventListener;
import cc.sukazyo.cono.morny.bot.api.InlineQueryUnit;
import com.pengrad.telegrambot.model.Update;
import com.pengrad.telegrambot.model.request.InlineQueryResult;
import com.pengrad.telegrambot.request.AnswerInlineQuery;
import javax.annotation.Nonnull;
import java.util.List;
/**
* telegram inlineQuery 功能的处理类
* 也是一个 InlineQueryManager还没做
*
* @since 0.4.1.3
*/
public class OnInlineQueries extends EventListener {
/**
* @since 0.4.1.3
*/
@Override
public boolean onInlineQuery (@Nonnull Update update) {
List<InlineQueryUnit<?>> results = MornyCoeur.queryManager().query(update);
int cacheTime = Integer.MAX_VALUE;
boolean isPersonal = InlineQueryUnit.DEFAULT_INLINE_PERSONAL_RESP;
InlineQueryResult<?>[] inlineQueryResults = new InlineQueryResult<?>[results.size()];
for (int i = 0; i < results.size(); i++) {
inlineQueryResults[i] = results.get(i).result;
if (cacheTime > results.get(i).cacheTime()) cacheTime = results.get(i).cacheTime();
if (results.get(i).isPersonal()) isPersonal = true;
}
if (results.size() == 0) return false;
MornyCoeur.extra().exec(new AnswerInlineQuery(
update.inlineQuery().id(), inlineQueryResults
).cacheTime(cacheTime).isPersonal(isPersonal));
return true;
}
}

View File

@ -1,42 +0,0 @@
package cc.sukazyo.cono.morny.bot.event;
import cc.sukazyo.cono.morny.MornyCoeur;
import cc.sukazyo.cono.morny.bot.api.EventListener;
import com.pengrad.telegrambot.model.Update;
import com.pengrad.telegrambot.request.SendMessage;
import javax.annotation.Nonnull;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static cc.sukazyo.cono.morny.util.CommonRandom.iif;
public class OnUserRandoms extends EventListener {
private static final Pattern USER_OR_QUERY = Pattern.compile("(.+)(?:还是|or)(.+)");
private static final Pattern USER_IF_QUERY = Pattern.compile("(.+)[吗?|]+$");
@Override
public boolean onMessage (@Nonnull Update update) {
if (update.message().text() == null) return false;
if (!update.message().text().startsWith("/")) return false;
final String query = update.message().text().substring(1);
String result = null;
Matcher matcher;
if ((matcher = USER_OR_QUERY.matcher(query)).find()) {
result = iif() ? matcher.group(1) : matcher.group(2);
} else if ((matcher = USER_IF_QUERY.matcher(query)).matches()) {
result = (iif()?"":"") + matcher.group(1);
}
if (result == null) return false;
MornyCoeur.extra().exec(new SendMessage(
update.message().chat().id(), result
).replyToMessageId(update.message().messageId()));
return true;
}
}

View File

@ -1,84 +0,0 @@
package cc.sukazyo.cono.morny.bot.event;
import cc.sukazyo.cono.morny.MornyCoeur;
import cc.sukazyo.cono.morny.bot.api.EventListener;
import cc.sukazyo.cono.morny.util.UniversalCommand;
import cc.sukazyo.cono.morny.util.tgapi.formatting.TGToString;
import com.pengrad.telegrambot.model.Message;
import com.pengrad.telegrambot.model.Update;
import com.pengrad.telegrambot.model.request.ParseMode;
import com.pengrad.telegrambot.request.SendMessage;
import javax.annotation.Nonnull;
import static cc.sukazyo.cono.morny.util.CommonConvert.stringsConnecting;
import static cc.sukazyo.cono.morny.util.tgapi.formatting.MsgEscape.escapeHtml;
public class OnUserSlashAction extends EventListener {
@Override
public boolean onMessage (@Nonnull Update event) {
final String text = event.message().text();
if (text == null) return false;
if (text.startsWith("/"))
{
/// Due to @Lapis_Apple, we stopped slash action function at .DP7 groups.
/// It may be enabled after some updates when the function will not be conflicted to other bots.
// if (event.message().chat().id() == ) return false;
//{ if (event.message().chat().title() != null && event.message().chat().title().contains(".DP7")) {
// logger.info(String.format("""
// Chat slash action ignored due to the following keyword.
// - %s
// - ".DP7\"""",
// TGToString.as(event.message().chat()).toStringFullNameId()
// ));
// return false;
// }
final String[] action = UniversalCommand.format(text);
action[0] = action[0].substring(1);
if (action[0].matches("^\\w+(@\\w+)?$")) {
return false; // 忽略掉 Telegram 命令格式的输入
} else if (action[0].contains("/")) {
return false; // 忽略掉疑似目录格式的输入
}
final boolean isHardParse = "".equals(action[0]);
/* 忽略空数据 */ if (isHardParse && action.length < 2) { return false; }
final String verb = isHardParse ? action[1] : action[0];
final boolean hasObject = action.length != (isHardParse?2:1);
final String object =
hasObject ?
stringsConnecting(action, " ", isHardParse?2:1, action.length-1) :
"";
final Message origin = event.message();
final Message target = (event.message().replyToMessage() == null ? (
origin
): (
event.message().replyToMessage()
));
MornyCoeur.extra().exec(new SendMessage(
event.message().chat().id(),
String.format(
"%s %s%s %s %s!",
TGToString.as(origin).getSenderFirstNameRefHtml(),
escapeHtml(verb), escapeHtml((hasObject?"":"")),
origin==target ?
"<a href='tg://user?id="+TGToString.as(target).getSenderId()+"'>自己</a>" :
TGToString.as(target).getSenderFirstNameRefHtml(),
escapeHtml(hasObject ? object+" " : "")
)
).parseMode(ParseMode.HTML).replyToMessageId(event.message().messageId()));
return true;
}
return false;
}
}

View File

@ -1,15 +0,0 @@
package cc.sukazyo.cono.morny.bot.query;
import javax.annotation.Nullable;
import cc.sukazyo.cono.morny.bot.api.InlineQueryUnit;
import com.pengrad.telegrambot.model.Update;
import java.util.List;
public interface ITelegramQuery {
@Nullable
List<InlineQueryUnit<?>> query (Update event);
}

View File

@ -1,31 +0,0 @@
package cc.sukazyo.cono.morny.bot.query;
import cc.sukazyo.cono.morny.bot.api.InlineQueryUnit;
import com.pengrad.telegrambot.model.Update;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.List;
public class MornyQueries {
private final List<ITelegramQuery> queryInstances = new ArrayList<>();
public MornyQueries () {
queryInstances.add(new RawText());
queryInstances.add(new MyInformation());
queryInstances.add(new ShareToolTwitter());
queryInstances.add(new ShareToolBilibili());
}
@Nonnull
public List<InlineQueryUnit<?>> query (@Nonnull Update event) {
final List<InlineQueryUnit<?>> results = new ArrayList<>();
for (ITelegramQuery instance : queryInstances) {
final List<InlineQueryUnit<?>> r = instance.query(event);
if (r!=null) results.addAll(r);
}
return results;
}
}

View File

@ -1,35 +0,0 @@
package cc.sukazyo.cono.morny.bot.query;
import javax.annotation.Nullable;
import cc.sukazyo.cono.morny.bot.api.InlineQueryUnit;
import com.pengrad.telegrambot.model.Update;
import com.pengrad.telegrambot.model.request.InlineQueryResultArticle;
import com.pengrad.telegrambot.model.request.InputTextMessageContent;
import com.pengrad.telegrambot.model.request.ParseMode;
import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramUserInformation;
import java.util.Collections;
import java.util.List;
import static cc.sukazyo.cono.morny.util.tgapi.formatting.NamedUtils.inlineIds;
public class MyInformation implements ITelegramQuery {
public static final String ID_PREFIX = "[morny/info/me]";
public static final String TITLE = "My Account Information";
@Override
@Nullable
public List<InlineQueryUnit<?>> query(Update event) {
if (!(event.inlineQuery().query() == null || "".equals(event.inlineQuery().query()))) return null;
return Collections.singletonList(new InlineQueryUnit<>(new InlineQueryResultArticle(
inlineIds(ID_PREFIX), TITLE,
new InputTextMessageContent(
TelegramUserInformation.informationOutputHTML(event.inlineQuery().from())
).parseMode(ParseMode.HTML)
)).isPersonal(true).cacheTime(10));
}
}

View File

@ -1,31 +0,0 @@
package cc.sukazyo.cono.morny.bot.query;
import cc.sukazyo.cono.morny.bot.api.InlineQueryUnit;
import javax.annotation.Nullable;
import com.pengrad.telegrambot.model.Update;
import com.pengrad.telegrambot.model.request.InlineQueryResultArticle;
import com.pengrad.telegrambot.model.request.InputTextMessageContent;
import java.util.Collections;
import java.util.List;
import static cc.sukazyo.cono.morny.util.tgapi.formatting.NamedUtils.inlineIds;
public class RawText implements ITelegramQuery {
public static final String ID_PREFIX = "[morny/r/text]";
public static final String TITLE = "Raw Text";
@Override
@Nullable
public List<InlineQueryUnit<?>> query (Update event) {
if (event.inlineQuery().query() == null || "".equals(event.inlineQuery().query())) return null;
return Collections.singletonList(new InlineQueryUnit<>(new InlineQueryResultArticle(
inlineIds(ID_PREFIX, event.inlineQuery().query()), TITLE,
new InputTextMessageContent(event.inlineQuery().query())
)));
}
}

View File

@ -1,80 +0,0 @@
package cc.sukazyo.cono.morny.bot.query;
import cc.sukazyo.cono.morny.bot.api.InlineQueryUnit;
import cc.sukazyo.cono.morny.util.BiliTool;
import com.pengrad.telegrambot.model.Update;
import com.pengrad.telegrambot.model.request.InlineQueryResultArticle;
import com.pengrad.telegrambot.model.request.InputTextMessageContent;
import com.pengrad.telegrambot.model.request.ParseMode;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static cc.sukazyo.cono.morny.Log.logger;
import static cc.sukazyo.cono.morny.util.tgapi.formatting.NamedUtils.inlineIds;
public class ShareToolBilibili implements ITelegramQuery {
public static final String TITLE_BILI_AV = "[bilibili] Share video / av";
public static final String TITLE_BILI_BV = "[bilibili] Share video / BV";
public static final String ID_PREFIX_BILI_AV = "[morny/share/bili/av]";
public static final String ID_PREFIX_BILI_BV = "[morny/share/bili/bv]";
public static final Pattern REGEX_BILI_VIDEO = Pattern.compile("^(?:(?:https?://)?(?:www\\.)?bilibili\\.com(?:/s)?/video/((?:av|AV)(\\d{1,12})|(?:bv|BV)([A-HJ-NP-Za-km-z1-9]{10}))/?(\\?(?:p=(\\d+))?.*)?|(?:av|AV)(\\d{1,12})|(?:bv|BV)([A-HJ-NP-Za-km-z1-9]{10}))$");
private static final String SHARE_FORMAT_HTML = "<a href='%s'>%s</a>";
@Nullable
@Override
public List<InlineQueryUnit<?>> query (Update event) {
if (event.inlineQuery().query() == null) return null;
final Matcher regex = REGEX_BILI_VIDEO.matcher(event.inlineQuery().query());
if (regex.matches()) {
logger.debug(String.format(
"====== Share Tool Bilibili Catch ok\n1: %s\n2: %s\n3: %s\n4: %s\n5: %s\n6: %s\n7: %s",
regex.group(1), regex.group(2), regex.group(3), regex.group(4),
regex.group(5), regex.group(6), regex.group(7)
));
// get video id from input, also get video part id
String av = regex.group(2)==null ? regex.group(6)==null ? null : regex.group(6) : regex.group(2);
String bv = regex.group(3)==null ? regex.group(7)==null ? null : regex.group(7) : regex.group(3);
logger.trace(String.format("catch id av[%s] bv[%s]", av, bv));
final int part = regex.group(5)==null ? -1 : Integer.parseInt(regex.group(5));
logger.trace(String.format("catch part [%s]", part));
if (av == null) {
assert bv != null;
av = String.valueOf(BiliTool.toAv(bv));
logger.trace(String.format("converted bv[%s] to av[%s]", bv, av));
} else {
bv = BiliTool.toBv(Long.parseLong(av));
logger.trace(String.format("converted av[%s] to bv[%s]", av, bv));
}
// build standard share links
final String linkPartParam = part==-1 ? "" : "?p="+part;
final String linkAv = "https://www.bilibili.com/video/av"+av + linkPartParam;
final String linkBv = "https://www.bilibili.com/video/BV"+bv + linkPartParam;
final String idAv = "av"+av;
final String idBv = "BV"+bv;
logger.trace("built all data.");
// build share message element
List<InlineQueryUnit<?>> result = new ArrayList<>();
result.add(new InlineQueryUnit<>(new InlineQueryResultArticle(
inlineIds(ID_PREFIX_BILI_AV+av), TITLE_BILI_AV+av,
new InputTextMessageContent(String.format(SHARE_FORMAT_HTML, linkAv, idAv)).parseMode(ParseMode.HTML)
)));
result.add(new InlineQueryUnit<>(new InlineQueryResultArticle(
inlineIds(ID_PREFIX_BILI_BV+bv), TITLE_BILI_BV+bv,
new InputTextMessageContent(String.format(SHARE_FORMAT_HTML, linkBv, idBv)).parseMode(ParseMode.HTML)
)));
return result;
}
return null;
}
}

View File

@ -1,50 +0,0 @@
package cc.sukazyo.cono.morny.bot.query;
import cc.sukazyo.cono.morny.bot.api.InlineQueryUnit;
import com.pengrad.telegrambot.model.Update;
import com.pengrad.telegrambot.model.request.InlineQueryResultArticle;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static cc.sukazyo.cono.morny.util.tgapi.formatting.NamedUtils.inlineIds;
public class ShareToolTwitter implements ITelegramQuery {
public static final String TITLE_VX = "[tweet] Share as VxTwitter";
public static final String TITLE_VX_COMBINED = "[tweet] Share as VxTwitter(combination)";
public static final String ID_PREFIX_VX = "[morny/share/twitter/vxtwi]";
public static final String ID_PREFIX_VX_COMBINED = "[morny/share/twitter/vxtwi_combine]";
public static final Pattern REGEX_TWEET_LINK = Pattern.compile(
"^(?:https?://)?((?:(?:c\\.)?vx|fx|www\\.)?twitter\\.com)/((\\w+)/status/(\\d+)(?:/photo/(\\d+))?)/?(\\?[\\w&=-]+)?$");
@Nullable
@Override
public List<InlineQueryUnit<?>> query (@Nonnull Update event) {
if (event.inlineQuery().query() == null) return null;
final Matcher regex = REGEX_TWEET_LINK.matcher(event.inlineQuery().query());
if (regex.matches()) {
List<InlineQueryUnit<?>> result = new ArrayList<>();
result.add(new InlineQueryUnit<>(new InlineQueryResultArticle(
inlineIds(ID_PREFIX_VX+event.inlineQuery().query()), TITLE_VX,
String.format("https://vxtwitter.com/%s", regex.group(2))
)));
result.add(new InlineQueryUnit<>(new InlineQueryResultArticle(
inlineIds(ID_PREFIX_VX_COMBINED+event.inlineQuery().query()), TITLE_VX_COMBINED,
String.format("https://c.vxtwitter.com/%s", regex.group(2))
)));
return result;
}
return null;
}
}

View File

@ -1,89 +0,0 @@
package cc.sukazyo.cono.morny.data.ip186;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
import javax.annotation.Nonnull;
import java.io.IOException;
/**
* 通过 {@value #SITE_URL} 进行 {@link #queryIp ip}/{@link #queryWhois whois} 数据查询的工具类
*
* @since 0.4.2.10
*/
public class IP186QueryHandler {
/**
* 请求所使用的 HTTP API 站点链接
* @since 0.4.2.10
*/
public static final String SITE_URL = "https://ip.186526.xyz/";
/**
* 进行 {@link #queryIp ip 查询}时所使用的 API 参数.<br>
* 目的使 API 直接返回原始数据
*/
private static final String QUERY_IP_PARAM = "type=json&format=true";
/**
* 进行 {@link #queryWhois whois 查询}时所使用的 API 参数.<br>
* 目的使 API 直接返回原始数据
*/
private static final String QUERY_WHOIS_PARAM = "type=plain";
/** 请求时使用的 OkHttp 请求工具实例 */
private static final OkHttpClient httpClient = new OkHttpClient();
/**
* 通过 {@value #SITE_URL} 获取 ip 信息.
* @see #QUERY_IP_PARAM 发送请求时所使用的 API 参数
* @param ip 需要进行查询的 ip
* @return 查询结果data 根据 {@value #SITE_URL} 的规则以 json 序列化
* @throws IOException 任何请求或解析错误
*/
@Nonnull
public static IP186QueryResponse queryIp (String ip) throws IOException {
final String requestUrl = SITE_URL + ip;
return commonQuery(requestUrl, QUERY_IP_PARAM);
}
/**
* 通过 {@value #SITE_URL} 获取域名信息.
* @see #QUERY_WHOIS_PARAM 发送请求时所使用的 API 参数
* @param domain 需要进行查询的域名
* @return 查询结果data 根据 {@value #SITE_URL} 的规则以 plain 序列化
* @throws IOException 任何请求或解析错误
*/
@Nonnull
public static IP186QueryResponse queryWhois (String domain) throws IOException {
final String requestUrl = SITE_URL + "whois/" + domain;
return commonQuery(requestUrl, QUERY_WHOIS_PARAM);
}
/**
* {@link #queryWhois(String)} 的结果进行裁剪.
* <br>
* 将会删除返回内容中 {@code >>> XXX <<<} 行以后的注释串
* 以达到只保留重要信息的目的
*
* @see #queryWhois(String)
*/
@Nonnull
public static IP186QueryResponse queryWhoisPretty (String domain) throws IOException {
final IP186QueryResponse raw = queryWhois(domain);
return new IP186QueryResponse(raw.url(), raw.body().substring(0, raw.body().indexOf("<<<")+3));
}
@Nonnull
private static IP186QueryResponse commonQuery (String requestUrl, String queryIpParam) throws IOException {
Request request = new Request.Builder().url(requestUrl + "?" + queryIpParam).build();
try (Response response = httpClient.newCall(request).execute()) {
final ResponseBody body = response.body();
if (body == null) throw new IOException("Null body.");
return new IP186QueryResponse(requestUrl, body.string());
}
}
}

View File

@ -1,11 +0,0 @@
package cc.sukazyo.cono.morny.data.ip186;
/**
* {@link IP186QueryHandler} 的请求结果数据的通用封装类.
*
* @since 0.4.2.10
* @param url 请求数据的<u>人类可读的</u>来源链接<b>并非api链接</b>
* @param body API 传回的数据内容
*/
public record IP186QueryResponse(String url, String body) {
}

View File

@ -58,7 +58,7 @@ public class MornyCoeur {
* morny 主程序启动时间<br>
* 用于统计数据
*/
public static final long coeurStartTimestamp = ServerMain.systemStartupTime;
public static final long coeurStartTimestamp = ServerMain.systemStartupTime();
private Object whileExitReason = null;

View File

@ -55,15 +55,15 @@ public class MornyCommands {
register(
new ON(),
new Hello(), /* new {@link HelloOnStart}, */
new MornyInfoOnHello(),
new GetUsernameAndId(),
new EventHack(),
new Nbnhhsh(),
new Ip186Query.Ip(),
new Ip186Query.Whois(),
new Encryptor(),
MornyInformation$.MODULE$,
GetUsernameAndId$.MODULE$,
EventHack$.MODULE$,
Nbnhhsh$.MODULE$,
IP186Query.IP$.MODULE$,
IP186Query.Whois$.MODULE$,
Encryptor$.MODULE$,
new SaveData(),
new MornyInformation(),
MornyInfoOnHello$.MODULE$,
new Version(),
new MornyRuntime(),
new Jrrp(),
@ -72,21 +72,21 @@ public class MornyCommands {
// 特殊的命令
register(
new Testing(),
new DirectMsgClear()
Testing$.MODULE$,
DirectMsgClear$.MODULE$
);
// 统一注册这些奇怪的东西&.&
register(
new 私わね(),
new 喵呜.Progynova()
私わね$.MODULE$,
喵呜.Progynova$.MODULE$
);
// special: 注册出于兼容使用的特别 event 的数据
OnUniMeowTrigger.register(
new 喵呜.抱抱(),
new 喵呜.揉揉(),
new 喵呜.蹭蹭(),
new 喵呜.贴贴()
喵呜.抱抱$.MODULE$,
喵呜.揉揉$.MODULE$,
喵呜.蹭蹭$.MODULE$,
喵呜.贴贴$.MODULE$
);
}

View File

@ -1,18 +1,15 @@
package cc.sukazyo.cono.morny.bot.event;
import cc.sukazyo.cono.morny.bot.api.EventListenerManager;
import cc.sukazyo.cono.morny.bot.api.InlineQueryUnit;
public class EventListeners {
public static final OnTelegramCommand COMMANDS_LISTENER = new OnTelegramCommand();
// public static final OnActivityRecord ACTIVITY_RECORDER = new OnActivityRecord();
public static final OnUserSlashAction USER_SLASH_ACTION = new OnUserSlashAction();
public static final OnUpdateTimestampOffsetLock UPDATE_TIMESTAMP_OFFSET_LOCK = new OnUpdateTimestampOffsetLock();
public static final OnInlineQueries INLINE_QUERY = new OnInlineQueries();
public static final OnCallMe CALL_ME = new OnCallMe();
public static final OnEventHackHandle EVENT_HACK_HANDLE = new OnEventHackHandle();
// static final OnKuohuanhuanNeedSleep KUOHUANHUAN_NEED_SLEEP = new OnKuohuanhuanNeedSleep();
public static final OnUserRandoms USER_RANDOMS = new OnUserRandoms();
public static final OnCallMsgSend CALL_MSG_SEND = new OnCallMsgSend();
public static final OnMedicationNotifyApply MEDICATION_NOTIFY_APPLY = new OnMedicationNotifyApply();
public static final OnRandomlyTriggered RANDOMLY_TRIGGERED = new OnRandomlyTriggered();
@ -28,11 +25,11 @@ public class EventListeners {
COMMANDS_LISTENER,
UNI_MEOW_TRIGGER,
RANDOMLY_TRIGGERED,
USER_RANDOMS,
OnUserRandom$.MODULE$,
QUESTION_MARK_REPLY,
USER_SLASH_ACTION,
INLINE_QUERY,
CALL_ME,
OnUserSlashAction$.MODULE$,
OnInlineQuery$.MODULE$,
OnCallMe$.MODULE$,
CALL_MSG_SEND,
MEDICATION_NOTIFY_APPLY,
EVENT_HACK_HANDLE

View File

@ -88,7 +88,7 @@ public class MornyReport {
as config fields:
%s
""",
MornyInformation.getVersionAllFullTagHtml(),
MornyInformation.getVersionAllFullTagHTML(),
MornyCoeur.getUsername(),
sectionConfigFields(MornyCoeur.config())
)

View File

@ -0,0 +1,7 @@
package cc.sukazyo.cono.morny.util.tgapi;
public class Standardize {
public static final int CHANNEL_SPEAKER_MAGIC_ID = 136817688;
}

View File

@ -1,6 +1,7 @@
package cc.sukazyo.cono.morny.util.tgapi.formatting;
import com.pengrad.telegrambot.model.Chat;
import com.pengrad.telegrambot.model.Message;
public class TGToStringFromChat {

View File

@ -0,0 +1,156 @@
package cc.sukazyo.cono.morny
import cc.sukazyo.cono.morny.Log.logger
import cc.sukazyo.cono.morny.MornyConfig.CheckFailure
import cc.sukazyo.cono.morny.util.CommonFormat
import java.time.ZoneOffset
import scala.collection.mutable.ArrayBuffer
import scala.language.postfixOps
object ServerMain {
private val THREAD_MORNY_INIT: String = "morny-init"
val systemStartupTime: Long = System.currentTimeMillis()
def main (args: Array[String]): Unit = {
val config = new MornyConfig.Prototype()
var mode_echoVersion = false
var mode_echoHello = false
var showHello = true
config.eventOutdatedTimestamp = systemStartupTime
val unknownArgs = ArrayBuffer[String]()
var i = 0
while (i < args.length) {
args(i) match {
case "-d" | "--dbg" | "--debug" => Log.debug(true)
case "--no-hello" | "-hf" | "--quiet" | "-q" => showHello = false
case "--only-hello" | "-ho" | "-o" | "-hi" => mode_echoHello = true
case "--version" | "-v" => mode_echoVersion = true
case "--outdated-block" | "-ob" => config.eventIgnoreOutdated = true
case "--api" | "-a" => i+=1 ; config.telegramBotApiServer = args(i)
case "--api-files" | "files-api" | "-af" => i+=1; config.telegramBotApiServer4File = args(i)
case "--token" | "-t" => i+=1 ; config.telegramBotKey = args(i)
case "--username" | "-u" => i+=1 ; config.telegramBotUsername = args(i)
case "--master" | "-mm" => i+=1 ; config.trustedMaster = args(i)toLong
case "--trusted-chat" | "-trs" => i+=1 ; config.trustedChat = args(i)toLong
case "--report-to" => i+=1; config.reportToChat = args(i)toLong
case "--trusted-reader-dinner" | "-trsd" => i+=1 ; config.dinnerTrustedReaders add (args(i)toLong)
case "--dinner-chat" | "-chd" => i+=1 ; config.dinnerChatId = args(i)toLong
case "--medication-notify-chat" | "-medc" => i+=1 ; config.medicationNotifyToChat = args(i)toLong
case "--medication-notify-timezone" | "-medtz" =>
i+=1
config.medicationTimerUseTimezone = ZoneOffset.ofHours(args(i)toInt)
case "--medication-notify-times" | "-medt" =>
i+=1
for (u <- args(i) split ",") {
config.medicationNotifyAt add (u toInt)
}
case "--auto-cmd-list" | "-ca" => config.commandLoginRefresh = true
case "--auto-cmd-remove" | "-cr" => config.commandLogoutClear = true
case "--auto-cmd" | "-cmd" | "-c" =>
config.commandLoginRefresh = true
config.commandLogoutClear = true
case _ => unknownArgs append args(i)
}
i+=1
}
/// Setup launch params from ENVIRONMENT
var propToken: String = null
var propTokenKey: String = null
for (iKey <- MornyConfig.PROP_TOKEN_KEY) {
if ((System getenv iKey) != null) {
propToken = System getenv iKey
propTokenKey = iKey
}
}
///
/// Output startup message
/// process startup params - like startup mode
///
if (showHello) logger info MornyAbout.MORNY_PREVIEW_IMAGE_ASCII
if (mode_echoHello) return;
if (unknownArgs.nonEmpty) logger warn
s"""Can't understand arg to some meaning
| ${unknownArgs mkString "\n "}"""
.stripMargin
if (Log debug)
logger warn
"""Debug log output enabled.
| It may lower your performance, make sure that you are not in production environment."""
.stripMargin
if (mode_echoVersion) {
logger info
s"""Morny Cono Version
|- version :
| Morny ${MornySystem.CODENAME toUpperCase}
| ${MornySystem.VERSION_BASE}${if (MornySystem.isUseDelta) "-δ"+MornySystem.VERSION_DELTA else ""}
|- md5hash :
| ${MornySystem.getJarMd5}
|- gitstat :
|${ if (MornySystem.isGitBuild) {
s""" on commit ${if (MornySystem.isCleanBuild) "- clean-build" else "<δ/non-clean-build>"}
| ${BuildConfig.COMMIT}"""
.stripMargin
} else " <non-git-build>"}
|- buildtd :
| ${BuildConfig.CODE_TIMESTAMP}
| ${CommonFormat.formatDate(BuildConfig.CODE_TIMESTAMP, 0)} [UTC]"""
.stripMargin
return
}
logger info
s"""ServerMain.java Loaded >>>
|- version ${MornySystem.VERSION_FULL}
|- Morny ${MornySystem.CODENAME toUpperCase}
|- <${MornySystem.getJarMd5}> [${BuildConfig.CODE_TIMESTAMP}]""".stripMargin
///
/// Check Coeur arguments
/// finally start Coeur Program
///
if (propToken != null) {
config.telegramBotKey = propToken
logger info s"Parameter <token> set by EnvVar $$$propTokenKey"
}
Thread.currentThread setName THREAD_MORNY_INIT
try
MornyCoeur.init(new MornyConfig(config))
catch {
case _: CheckFailure.NullTelegramBotKey =>
logger.info("Parameter required has no value:\n --token.")
case e: CheckFailure =>
logger.error("Unknown failure occurred while starting ServerMain!:")
e.printStackTrace(System.out)
}
}
}

View File

@ -0,0 +1,54 @@
package cc.sukazyo.cono.morny.bot.command
import cc.sukazyo.cono.morny.Log.logger
import cc.sukazyo.cono.morny.MornyCoeur
import cc.sukazyo.cono.morny.data.TelegramStickers
import cc.sukazyo.cono.morny.util.tgapi.InputCommand
import com.pengrad.telegrambot.model.{Chat, Update}
import com.pengrad.telegrambot.request.{DeleteMessage, GetChatMember, SendSticker}
import scala.language.postfixOps
object DirectMsgClear extends ISimpleCommand {
override def getName: String = "r"
override def getAliases: Array[String] = null
override def execute (command: InputCommand, event: Update): Unit = {
logger debug "executing command /r"
if (event.message.replyToMessage == null) return;
logger trace "message is a reply"
if (event.message.replyToMessage.from.id != MornyCoeur.getUserid) return;
logger trace "message replied is from me"
if (System.currentTimeMillis/1000 - event.message.replyToMessage.date > 48*60*60) return;
logger trace "message is not outdated(48 hrs ago)"
val isTrusted = MornyCoeur.trustedInstance isTrusted event.message.from.id
def _isReplyTrusted: Boolean =
if (event.message.replyToMessage.replyToMessage == null) false
else if (event.message.replyToMessage.replyToMessage.from.id == event.message.from.id) true
else false
if (isTrusted || _isReplyTrusted) {
MornyCoeur.extra exec DeleteMessage(
event.message.chat.id, event.message.replyToMessage.messageId
)
def _isPrivate: Boolean = event.message.chat.`type` == Chat.Type.Private
def _isPermission: Boolean =
(MornyCoeur.extra exec GetChatMember(event.message.chat.id, event.message.from.id))
.chatMember.canDeleteMessages
if (_isPrivate || _isPermission) {
MornyCoeur.extra exec DeleteMessage(event.message.chat.id, event.message.messageId)
}
} else MornyCoeur.extra exec SendSticker(
event.message.chat.id,
TelegramStickers ID_403
).replyToMessageId(event.message.messageId)
}
}

View File

@ -0,0 +1,178 @@
package cc.sukazyo.cono.morny.bot.command
import cc.sukazyo.cono.morny.Log.logger
import cc.sukazyo.cono.morny.MornyCoeur
import cc.sukazyo.cono.morny.daemon.MornyReport
import cc.sukazyo.cono.morny.data.TelegramStickers
import cc.sukazyo.cono.morny.util.CommonConvert.byteArrayToHex
import cc.sukazyo.cono.morny.util.CommonEncrypt
import cc.sukazyo.cono.morny.util.CommonEncrypt.*
import cc.sukazyo.cono.morny.util.tgapi.InputCommand
import com.pengrad.telegrambot.model.{PhotoSize, Update}
import com.pengrad.telegrambot.model.request.ParseMode
import com.pengrad.telegrambot.request.{GetFile, SendDocument, SendMessage, SendSticker}
import java.io.IOException
import java.util.Base64
import scala.language.postfixOps
object Encryptor extends ITelegramCommand {
override def getName: String = "encrypt"
override def getAliases: Array[String] = null
override def getParamRule: String = "[algorithm|(l)] [(uppercase)]"
override def getDescription: String = "通过指定算法加密回复的内容 (目前只支持文本)"
override def execute (command: InputCommand, event: Update): Unit = {
val args = command.getArgs
if ((args isEmpty) || ((args(0) equals "l") && (args.length == 1)))
echoHelp(event.message.chat.id, event.message.messageId)
return
def _is_mod_u(arg: String): Boolean =
if (arg equalsIgnoreCase "uppercase") return true
if (arg equalsIgnoreCase "u") return true
if (arg equalsIgnoreCase "upper") return true
false
val mod_uppercase = if (args.length > 1) {
if (args.length < 3 && _is_mod_u(args(1))) true
else
MornyCoeur.extra exec SendSticker(
event.message.chat.id,
TelegramStickers ID_404
).replyToMessageId(event.message.messageId)
return
} else false
trait XEncryptable { val asByteArray: Array[Byte] }
case class XFile (data: Array[Byte], name: String) extends XEncryptable {
val asByteArray: Array[Byte] = data
}
case class XText (data: String) extends XEncryptable {
val asByteArray: Array[Byte] = data getBytes CommonEncrypt.ENCRYPT_STANDARD_CHARSET
}
val input: XEncryptable =
val _r = event.message.replyToMessage
if ((_r ne null) && (_r.document ne null)) {
try {XFile(
MornyCoeur.getAccount getFileContent (MornyCoeur.extra exec GetFile(_r.document.fileId)).file,
_r.document.fileName
)} catch case e: IOException =>
logger warn s"NetworkRequest error: TelegramFileAPI:\n\t${e.getMessage}"
MornyReport.exception(e, "NetworkRequest error: TelegramFileAPI")
return
} else if ((_r ne null) && (_r.photo ne null)) {
try {
var _photo_origin: PhotoSize = null
var _photo_size: Long = 0
for (size <- _r.photo)
val _size = (size.width longValue)*size.height
if (_photo_size < _size)
_photo_origin = size
_photo_size = _size
if (_photo_origin eq null) throw IllegalArgumentException("no photo from api.")
XFile(
MornyCoeur.getAccount getFileContent (MornyCoeur.extra exec GetFile(_photo_origin.fileId)).file,
s"photo${byteArrayToHex(hashMd5(System.currentTimeMillis toString)) substring 32-12 toUpperCase}.png"
)
} catch
case e: IOException =>
logger warn s"NetworkRequest error: TelegramFileAPI:\n\t${e.getMessage}"
MornyReport.exception(e, "NetworkRequest error: TelegramFileAPI")
return
case e: IllegalArgumentException =>
logger warn s"FileProcess error: PhotoSize:\n\t${e.getMessage}"
MornyReport.exception(e, "FileProcess error: PhotoSize")
return
} else if ((_r ne null) && (_r.text ne null)) {
XText(_r.text)
} else {
MornyCoeur.extra exec SendMessage(
event.message.chat.id,
"<i><u>null</u></i>"
).parseMode(ParseMode HTML).replyToMessageId(event.message.messageId)
return
}
trait EXTextLike { val text: String }
case class EXFile (result: Array[Byte], resultName: String)
case class EXText (result: String) extends EXTextLike { override val text:String = result }
case class EXHash (result: String) extends EXTextLike { override val text:String = result }
def genResult_encrypt (source: XEncryptable, processor: Array[Byte]=>Array[Byte], filenameProcessor: String=>String): EXFile|EXText = {
source match
case x_file: XFile => EXFile(processor(x_file asByteArray), filenameProcessor(x_file.name))
case x: XText => EXText(String(processor(x asByteArray), ENCRYPT_STANDARD_CHARSET))
}
def genResult_hash (source: XEncryptable, processor: Array[Byte]=>Array[Byte]): EXHash =
val hashed = byteArrayToHex(processor(source asByteArray))
EXHash(if mod_uppercase then hashed toUpperCase else hashed)
val result: EXHash|EXFile|EXText = args(0) match
case "base64" | "b64" | "base64url" | "base64u" | "b64u" =>
val _tool_b64 =
if args(0) contains "u" then Base64.getUrlEncoder
else Base64.getEncoder
genResult_encrypt(
input,
_tool_b64.encode,
n => n+".b64.txt"
)
case "base64decode" | "base64d" | "b64d" | "base64url-decode" | "base64ud" | "b64ud" =>
val _tool_b64d =
if args(0) contains "u" then Base64.getUrlDecoder
else Base64.getDecoder
try { genResult_encrypt(
input,
_tool_b64d.decode,
CommonEncrypt.base64FilenameLint
) } catch case _: IllegalArgumentException =>
MornyCoeur.extra exec SendSticker(
event.message.chat.id,
TelegramStickers ID_404 // todo: is here better erro notify?
).replyToMessageId(event.message.messageId)
return
case "md5" => genResult_hash(input, hashMd5)
case "sha1" => genResult_hash(input, hashSha1)
case "sha256" => genResult_hash(input, hashSha256)
case "sha512" => genResult_hash(input, hashSha512)
case _ =>
MornyCoeur.extra exec SendSticker(
event.message.chat.id,
TelegramStickers ID_404
).replyToMessageId(event.message.messageId)
return;
result match
case _file: EXFile =>
MornyCoeur.extra exec SendDocument(
event.message.chat.id,
_file.result
).fileName(_file.resultName).replyToMessageId(event.message.messageId)
case _text: EXTextLike =>
import cc.sukazyo.cono.morny.util.tgapi.formatting.MsgEscape.escapeHtml as h
MornyCoeur.extra exec SendMessage(
event.message.chat.id,
s"<pre><code>${h(_text.text)}</code></pre>"
).parseMode(ParseMode HTML).replyToMessageId(event.message.messageId)
}
private def echoHelp(chat: Long, replyTo: Int): Unit =
MornyCoeur.extra exec SendMessage(
chat,
s"""<b><u>base64</u></b>, b64
|<b><u>base64url</u></b>, base64u, b64u
|<b><u>base64decode</u></b>, base64d, b64d
|<b><u>base64url-decode</u></b>, base64ud, b64ud
|<b><u>sha1</u></b>
|<b><u>sha256</u></b>
|<b><u>sha512</u></b>
|<b><u>md5</u></b>
|---
|<b><i>uppercase</i></b>, upper, u <i>(sha1/sha256/sha512/md5 only)</i>"""
.stripMargin
).replyToMessageId(replyTo).parseMode(ParseMode HTML)
}

View File

@ -0,0 +1,56 @@
package cc.sukazyo.cono.morny.bot.command
import cc.sukazyo.cono.morny.MornyCoeur
import cc.sukazyo.cono.morny.bot.event.OnEventHackHandle
import cc.sukazyo.cono.morny.util.tgapi.InputCommand
import com.pengrad.telegrambot.model.Update
import OnEventHackHandle.{HackType, registerHack}
import cc.sukazyo.cono.morny.data.TelegramStickers
import com.pengrad.telegrambot.request.SendSticker
import scala.language.postfixOps
object EventHack extends ITelegramCommand {
override def getName: String = "event_hack"
override def getAliases: Array[String] = null
override def getParamRule: String = "[(user|group|any)]"
override def getDescription: String = "输出 bot 下一个获取到的事件序列化数据"
override def execute (command: InputCommand, event: Update): Unit = {
val x_mode = if (command.hasArgs) command.getArgs()(0) else ""
def done_ok =
MornyCoeur.extra exec SendSticker(
event.message.chat.id,
TelegramStickers ID_WAITING
).replyToMessageId(event.message.messageId)
def done_forbiddenForAny =
MornyCoeur.extra exec SendSticker(
event.message.chat.id,
TelegramStickers ID_403
).replyToMessageId(event.message.messageId)
def doRegister (t: HackType): Unit =
registerHack(
event.message.messageId longValue,
event.message.from.id,
event.message.chat.id,
t
)
x_mode match
case "any" =>
if (MornyCoeur.trustedInstance isTrusted event.message.from.id)
doRegister(HackType ANY)
done_ok
else done_forbiddenForAny
case "group" =>
doRegister(HackType GROUP)
done_ok
case _ =>
doRegister(HackType USER)
done_ok
}
}

View File

@ -0,0 +1,66 @@
package cc.sukazyo.cono.morny.bot.command
import cc.sukazyo.cono.morny.MornyCoeur
import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramUserInformation
import cc.sukazyo.cono.morny.util.tgapi.{InputCommand, Standardize}
import com.pengrad.telegrambot.model.Update
import com.pengrad.telegrambot.model.request.ParseMode
import com.pengrad.telegrambot.request.{GetChatMember, SendMessage}
import scala.language.postfixOps
object GetUsernameAndId extends ITelegramCommand {
override def getName: String = "user"
override def getAliases: Array[String] = Array()
override def getParamRule: String = "[userid]"
override def getDescription: String = "获取指定或回复的用户相关信息"
override def execute (command: InputCommand, event: Update): Unit = {
val args = command.getArgs
if (args.length > 1)
MornyCoeur.extra exec SendMessage(
event.message.chat.id,
"[Unavailable] Too much arguments."
).replyToMessageId(event.message.messageId)
return
val userId: Long =
if (args nonEmpty) {
try args(0) toLong
catch case e: NumberFormatException =>
MornyCoeur.extra exec SendMessage(
event.message.chat.id,
s"[Unavailable] ${e.getMessage}"
).replyToMessageId(event.message.messageId)
return
} else if (event.message.replyToMessage eq null) event.message.from.id
else event.message.replyToMessage.from.id
val response = MornyCoeur.getAccount execute GetChatMember(event.message.chat.id, userId)
if (response.chatMember eq null)
MornyCoeur.extra exec SendMessage(
event.message.chat.id,
"[Unavailable] user not found."
).replyToMessageId(event.message.messageId)
return
val user = response.chatMember.user
if (user.id eq Standardize.CHANNEL_SPEAKER_MAGIC_ID)
MornyCoeur.extra exec SendMessage(
event.message.chat.id,
"<code>$__channel_identify</code>"
).replyToMessageId(event.message.messageId)
return;
MornyCoeur.extra exec SendMessage(
event.message.chat.id,
TelegramUserInformation informationOutputHTML user
).replyToMessageId(event.message.messageId()).parseMode(ParseMode HTML)
}
}

View File

@ -0,0 +1,77 @@
package cc.sukazyo.cono.morny.bot.command
import cc.sukazyo.cono.morny.MornyCoeur
import cc.sukazyo.cono.morny.data.ip186.IP186QueryHandler
import cc.sukazyo.cono.morny.util.tgapi.InputCommand
import com.pengrad.telegrambot.model.Update
import com.pengrad.telegrambot.model.request.ParseMode
import com.pengrad.telegrambot.request.SendMessage
import scala.language.postfixOps
object IP186Query {
private enum Subs (val cmd: String):
case IP extends Subs("ip")
case WHOIS extends Subs("whois")
object IP extends ITelegramCommand:
override def getName: String = "ip"
override def getAliases: Array[String] = null
override def getParamRule: String = "[ip]"
override def getDescription: String = "通过 https://ip.186526.xyz 查询 ip 资料"
override def execute (command: InputCommand, event: Update): Unit = query(event, command)
object Whois extends ITelegramCommand:
override def getName: String = "whois"
override def getAliases: Array[String] = null
override def getParamRule: String = "[domain]"
override def getDescription: String = "通过 https://ip.186526.xyz 查询域名资料"
override def execute (command: InputCommand, event: Update): Unit = query(event, command)
private def query (event: Update, command: InputCommand): Unit = {
val target: String|Null =
if (command.getArgs isEmpty)
if event.message.replyToMessage eq null then null else event.message.replyToMessage.text
else if (command.getArgs.length > 1)
MornyCoeur.extra exec SendMessage(
event.message.chat.id,
"[Unavailable] Too much arguments."
).replyToMessageId(event.message.messageId)
return
else command.getArgs()(0)
if (target eq null)
MornyCoeur.extra exec new SendMessage(
event.message.chat.id,
"[Unavailable] No ip defined."
).replyToMessageId(event.message.messageId)
return;
try {
val response = command.getCommand match
case Subs.IP.cmd => IP186QueryHandler.query_ip(target)
case Subs.WHOIS.cmd => IP186QueryHandler.query_whoisPretty(target)
case _ => throw IllegalArgumentException(s"Unknown 186-IP query method ${command.getCommand}")
import cc.sukazyo.cono.morny.util.tgapi.formatting.MsgEscape.escapeHtml as h
MornyCoeur.extra exec SendMessage(
event.message.chat.id,
s"""${h(response.url)}
|<code>${h(response.body)}</code>"""
.stripMargin
).parseMode(ParseMode HTML).replyToMessageId(event.message.messageId)
} catch case e: Exception =>
import cc.sukazyo.cono.morny.util.tgapi.formatting.MsgEscape.escapeHtml as h
MornyCoeur.extra().exec(new SendMessage(
event.message().chat().id(),
s"""[Exception] in query:
|<code>${h(e.getMessage)}</code>"""
.stripMargin
).parseMode(ParseMode.HTML).replyToMessageId(event.message().messageId()));
}
}

View File

@ -0,0 +1,35 @@
package cc.sukazyo.cono.morny.bot.command
import cc.sukazyo.cono.morny.MornyCoeur
import cc.sukazyo.cono.morny.util.tgapi.InputCommand
import com.pengrad.telegrambot.model.Update
import com.pengrad.telegrambot.model.request.ParseMode
import com.pengrad.telegrambot.request.SendPhoto
import scala.language.postfixOps
object MornyInfoOnHello extends ISimpleCommand {
override def getName: String = "start"
override def getAliases: Array[String] = Array()
override def execute (command: InputCommand, event: Update): Unit = {
MornyCoeur.extra exec new SendPhoto(
event.message.chat.id,
MornyInformation.getAboutPic
).caption(
s"""欢迎使用 <b>Morny Cono</b><i>来自安妮的侍从小鼠</i>。
|Morny 具有各种各样的功能
|
|
|${MornyInformation.getMornyAboutLinksHTML}
|
|
|你可以随时通过 /info 重新获得这些信息"""
.stripMargin
).parseMode(ParseMode HTML)
}
}

View File

@ -0,0 +1,167 @@
package cc.sukazyo.cono.morny.bot.command
import cc.sukazyo.cono.morny.data.{TelegramImages, TelegramStickers}
import cc.sukazyo.cono.morny.util.CommonFormat.{formatDate, formatDuration}
import cc.sukazyo.cono.morny.util.tgapi.InputCommand
import cc.sukazyo.cono.morny.util.tgapi.formatting.MsgEscape.escapeHtml as h
import cc.sukazyo.cono.morny.{BuildConfig, MornyAbout, MornyCoeur, MornySystem}
import com.pengrad.telegrambot.model.Update
import com.pengrad.telegrambot.model.request.ParseMode
import com.pengrad.telegrambot.request.{SendMessage, SendPhoto, SendSticker}
import java.lang.System
import java.net.InetAddress
import java.rmi.UnknownHostException
import scala.language.postfixOps
object MornyInformation extends ITelegramCommand {
private case object Subs {
val STICKERS = "stickers"
val RUNTIME = "runtime"
val VERSION = "version"
val VERSION_2 = "v"
}
override def getName: String = "info"
override def getAliases: Array[String] = Array()
override def getParamRule: String = "[(version|runtime|stickers[.IDs])]"
override def getDescription: String = "输出当前 Morny 的各种信息"
override def execute (command: InputCommand, event: Update): Unit = {
if (!command.hasArgs) {
echoInfo(event.message.chat.id, event.message.messageId)
return
}
val action: String = command.getArgs()(0)
action match {
case Subs.STICKERS => echoStickers(command, event)
case Subs.RUNTIME => echoRuntime(event)
case Subs.VERSION | Subs.VERSION_2 => echoVersion(event)
case _ => echo404(event)
}
}
def getVersionGitTagHTML: String = {
if (!MornySystem.isGitBuild) return ""
val g = StringBuilder()
val cm = BuildConfig.COMMIT substring(0, 8)
val cp = MornySystem.currentCodePath
if (cp == null) g++= s"<code>$cm</code>"
else g++= s"<a href='$cp'>$cm</a>"
if (!MornySystem.isCleanBuild) g++= ".<code>δ</code>"
g toString
}
def getVersionAllFullTagHTML: String = {
val v = StringBuilder()
v ++= s"<code>${MornySystem VERSION_BASE}</code>"
if (MornySystem isUseDelta) v++=s"-δ<code>${MornySystem VERSION_DELTA}</code>"
if (MornySystem isGitBuild) v++="+"++=getVersionGitTagHTML
v ++= s"*<code>${MornySystem.CODENAME toUpperCase}</code>"
v toString
}
def getRuntimeHostname: String|Null = {
try InetAddress.getLocalHost.getHostName
catch case _:UnknownHostException => null
}
def getAboutPic: Array[Byte] = TelegramImages.IMG_ABOUT get
def getMornyAboutLinksHTML: String =
s"""<a href='${MornyAbout MORNY_SOURCECODE_LINK}'>source code</a> | <a href='${MornyAbout MORNY_SOURCECODE_SELF_HOSTED_MIRROR_LINK}'>backup</a>
|<a href='${MornyAbout MORNY_ISSUE_TRACKER_LINK}'>反馈 / issue tracker</a>
|<a href='${MornyAbout MORNY_USER_GUIDE_LINK}'>使用说明书 / user guide & docs</a>"""
.stripMargin
private def echoInfo (chatId: Long, replyTo: Int): Unit = {
MornyCoeur.extra exec new SendPhoto(
chatId,
getAboutPic
).caption(
s"""<b>Morny Cono</b>
|来自安妮的侍从小鼠
|
|$getMornyAboutLinksHTML"""
.stripMargin
).parseMode(ParseMode HTML).replyToMessageId(replyTo)
}
private def echoStickers (command: InputCommand, event: Update): Unit = {
val chat = event.message.chat.id
val replyTo = event.message.messageId
var sid: String|Null = null
if (command.getArgs()(0) eq Subs.STICKERS) {
if (command.getArgs.length == 1) sid = ""
else if (command.getArgs.length == 2) sid = command.getArgs()(1)
} else if (command.getArgs.length == 1) {
if ((command.getArgs()(0) startsWith s"${Subs.STICKERS}.") || (command.getArgs()(0) startsWith s"${Subs.STICKERS}#")) {
sid = command.getArgs()(0) substring Subs.STICKERS.length+1
}
}
if (sid == null) echo404(event)
else echoStickers(sid, chat, replyTo)
}
private def echoStickers (sid: String, send_chat: Long, send_replyTo: Int): Unit = {
if (sid isEmpty) TelegramStickers echoAllStickers(MornyCoeur.extra, send_chat, send_replyTo)
else TelegramStickers echoStickerByID(sid, MornyCoeur.extra, send_chat, send_replyTo)
}
private[command] def echoVersion (event: Update): Unit = {
val versionDeltaHTML = if (MornySystem.isUseDelta) s"-δ<code>${h(MornySystem.VERSION_DELTA)}</code>" else ""
val versionGitHTML = if (MornySystem.isGitBuild) s"git $getVersionGitTagHTML" else ""
MornyCoeur.extra exec new SendMessage(
event.message.chat.id,
s"""version:
|- Morny <code>${h(MornySystem.CODENAME toUpperCase)}</code>
|- <code>${h(MornySystem.VERSION_BASE)}</code>$versionDeltaHTML${if (MornySystem.isGitBuild) "\n- " + versionGitHTML else ""}
|coeur md5_hash:
|- <code>${h(MornySystem.getJarMd5)}</code>
|coding timestamp:
|- <code>${BuildConfig.CODE_TIMESTAMP}</code>
|- <code>${h(formatDate(BuildConfig.CODE_TIMESTAMP, 0))} [UTC]</code>
|""".stripMargin
).replyToMessageId(event.message.messageId).parseMode(ParseMode HTML)
}
private[command] def echoRuntime (event: Update): Unit = {
def sysprop (p: String): String = System.getProperty(p)
MornyCoeur.extra exec new SendMessage(
event.message.chat.id,
/* html */
s"""system:
|- Morny <code>${h(if (getRuntimeHostname == null) "<unknown-host>" else getRuntimeHostname)}</code>
|- <code>${h(sysprop("os.name"))}</code> <code>${h(sysprop("os.arch"))}</code> <code>${h(sysprop("os.version"))}</code>
|java runtime:
|- <code>${h(sysprop("java.vm.vendor"))}.${h(sysprop("java.vm.name"))}</code>
|- <code>${h(sysprop("java.vm.version"))}</code>
|vm memory:
|- <code>${Runtime.getRuntime.totalMemory/1024/1024}</code> / <code>${Runtime.getRuntime.maxMemory/1024/1024}</code>
|- <code>${Runtime.getRuntime.availableProcessors}</code> cores
|coeur version:
|- $getVersionAllFullTagHTML
|- <code>${h(MornySystem.getJarMd5)}</code>
|- <code>${h(formatDate(BuildConfig.CODE_TIMESTAMP, 0))} [UTC]</code>
|- [<code>${BuildConfig.CODE_TIMESTAMP}</code>]
|continuous:
|- <code>${h(formatDuration(System.currentTimeMillis - MornyCoeur.coeurStartTimestamp))}</code>
|- [<code>${System.currentTimeMillis - MornyCoeur.coeurStartTimestamp}</code>]
|- <code>${h(formatDate(MornyCoeur.coeurStartTimestamp, 0))}</code>
|- [<code>${MornyCoeur.coeurStartTimestamp}</code>]"""
.stripMargin
).parseMode(ParseMode HTML).replyToMessageId(event.message.messageId)
}
private def echo404 (event: Update): Unit =
MornyCoeur.extra exec new SendSticker(
event.message.chat.id,
TelegramStickers ID_404
).replyToMessageId(event.message.messageId)
}

View File

@ -0,0 +1,85 @@
package cc.sukazyo.cono.morny.bot.command
import cc.sukazyo.cono.morny.MornyCoeur
import cc.sukazyo.cono.morny.data.{NbnhhshQuery, TelegramStickers}
import cc.sukazyo.cono.morny.util.tgapi.InputCommand
import cc.sukazyo.cono.morny.util.tgapi.formatting.MsgEscape.escapeHtml as h
import com.pengrad.telegrambot.model.Update
import com.pengrad.telegrambot.model.request.ParseMode
import com.pengrad.telegrambot.request.{SendMessage, SendSticker}
import java.io.IOException
import scala.language.postfixOps
object Nbnhhsh extends ITelegramCommand {
private val NBNHHSH_RESULT_HEAD_HTML = "<a href=\"https://lab.magiconch.com/nbnhhsh/\">## Result of nbnhhsh query :</a>"
override def getName: String = "nbnhhsh"
override def getAliases: Array[String]|Null = null
override def getParamRule: String = "[text]"
override def getDescription: String = "检索文本内 nbnhhsh 词条"
override def execute (command: InputCommand, event: Update): Unit = {
val queryTarget: String|Null =
import cc.sukazyo.cono.morny.util.CommonConvert.stringsConnecting
if (event.message.replyToMessage != null && event.message.replyToMessage.text != null)
event.message.replyToMessage.text
else if command hasArgs then
stringsConnecting(command.getArgs, " ", 0, command.getArgs.length-1)
else null
if (queryTarget == null)
MornyCoeur.extra exec SendSticker(
event.message.chat.id,
TelegramStickers ID_404
).replyToMessageId(event.message.messageId)
return;
try {
val queryResp = NbnhhshQuery sendGuess queryTarget
val message = StringBuilder(NBNHHSH_RESULT_HEAD_HTML)
import cc.sukazyo.cono.morny.Log.logger
logger debug s"**xx len=${queryResp.words.length}"
for (_word <- queryResp.words) {
logger debug "**exec"
if ((_word.trans ne null) && (_word.trans isEmpty)) _word.trans = null
if ((_word.inputting ne null) && (_word.inputting isEmpty)) _word.inputting = null
if ((_word.trans ne null) || (_word.inputting ne null))
message ++= s"\n\n<b>[[ ${h(_word.name)} ]]</b>"
logger debug s"**used [${_word.name}]"
if (_word.trans != null) for (_trans <- _word.trans)
message ++= s"\n* <i>${h(_trans)}</i>"
logger debug s"**used [${_word.name}] used `${_trans}``"
if (_word.inputting != null)
logger debug s"**used [${_word.name}] inputting"
if (_word.trans != null)
message += '\n'
message ++= " maybe:"
for (_inputting <- _word.inputting)
logger debug s"**used [${_word.name}] used-i ${_inputting}"
message ++= s"\n` <i>${h(_inputting)}</i>"
logger debug s"**exec as ${_word.name}"
}
MornyCoeur.extra exec SendMessage(
event.message.chat.id,
message toString
).parseMode(ParseMode HTML).replyToMessageId(event.message.messageId)
} catch case e: IOException => {
MornyCoeur.extra exec SendMessage(
event.message.chat.id,
s"""[Exception] in query:
|${h(e.getMessage)}
|""".stripMargin
)
}
}
}

View File

@ -0,0 +1,30 @@
package cc.sukazyo.cono.morny.bot.command
import cc.sukazyo.cono.morny.MornyCoeur
import cc.sukazyo.cono.morny.util.tgapi.InputCommand
import com.pengrad.telegrambot.model.Update
import com.pengrad.telegrambot.model.request.ParseMode
import com.pengrad.telegrambot.request.SendMessage
import javax.annotation.Nonnull
import javax.annotation.Nullable
import scala.language.postfixOps
object Testing extends ISimpleCommand {
override def getName: String = "test"
override def getAliases: Array[String] = null
override def execute (command: InputCommand, event: Update): Unit = {
val a = StringBuilder("value")
a ++= "Changed"
MornyCoeur.extra exec new SendMessage(
event.message.chat.id,
"<b>Just</b> a TEST command. num is:" + (a toString)
).replyToMessageId(event.message.messageId).parseMode(ParseMode HTML)
}
}

View File

@ -0,0 +1,67 @@
package cc.sukazyo.cono.morny.bot.command
import cc.sukazyo.cono.morny.MornyCoeur
import cc.sukazyo.cono.morny.data.TelegramStickers
import cc.sukazyo.cono.morny.util.tgapi.InputCommand
import com.pengrad.telegrambot.model.request.ParseMode
import com.pengrad.telegrambot.model.{Message, Update}
import com.pengrad.telegrambot.request.{SendMessage, SendSticker}
import javax.swing.text.html.HTML
import scala.annotation.unused
import scala.language.postfixOps
@SuppressWarnings(Array("NonAsciiCharacters"))
object 喵呜 {
object 抱抱 extends ISimpleCommand {
override def getName: String = "抱抱"
override def getAliases: Array[String] = Array()
override def execute (command: InputCommand, event: Update): Unit =
replyingSet(event, "贴贴", "贴贴")
}
object 揉揉 extends ISimpleCommand {
override def getName: String = "揉揉"
override def getAliases: Array[String] = Array()
override def execute (command: InputCommand, event: Update): Unit =
replyingSet(event, "蹭蹭", "摸摸")
}
object 蹭蹭 extends ISimpleCommand {
override def getName: String = "蹭蹭"
override def getAliases: Array[String] = Array()
override def execute (command: InputCommand, event: Update): Unit =
replyingSet(event, "揉揉", "蹭蹭")
}
object 贴贴 extends ISimpleCommand {
override def getName: String = "贴贴"
override def getAliases: Array[String] = Array()
override def execute (command: InputCommand, event: Update): Unit =
replyingSet(event, "贴贴", "贴贴")
}
object Progynova extends ITelegramCommand {
override def getName: String = "install"
override def getAliases: Array[String] = Array()
override def getParamRule: String = ""
override def getDescription: String = "抽取一个神秘盒子"
override def execute (command: InputCommand, event: Update): Unit = {
MornyCoeur.extra exec new SendSticker(
event.message.chat.id,
TelegramStickers ID_PROGYNOVA
).replyToMessageId(event.message.messageId)
}
}
private def replyingSet (event: Update, whileRec: String, whileNew: String): Unit = {
val isNew = event.message.replyToMessage == null;
val target = if (isNew) event.message else event.message.replyToMessage
MornyCoeur.extra exec new SendMessage(
event.message.chat.id,
if (isNew) whileNew else whileRec
).replyToMessageId(target.messageId).parseMode(ParseMode HTML)
}
}

View File

@ -0,0 +1,26 @@
package cc.sukazyo.cono.morny.bot.command
import cc.sukazyo.cono.morny.MornyCoeur
import cc.sukazyo.cono.morny.util.tgapi.InputCommand
import cc.sukazyo.cono.morny.util.CommonRandom.probabilityTrue
import com.pengrad.telegrambot.model.Update
import com.pengrad.telegrambot.request.SendMessage
object 私わね extends ISimpleCommand {
override def getName: String = "me"
override def getAliases: Array[String] = Array()
override def execute (command: InputCommand, event: Update): Unit = {
if (probabilityTrue(521)) {
val text = "/打假"
MornyCoeur.extra exec new SendMessage(
event.message.chat.id,
text
).replyToMessageId(event.message.messageId)
}
}
}

View File

@ -0,0 +1,90 @@
package cc.sukazyo.cono.morny.bot.event
import cc.sukazyo.cono.morny.Log.logger
import cc.sukazyo.cono.morny.MornyCoeur
import cc.sukazyo.cono.morny.bot.api.EventListener
import cc.sukazyo.cono.morny.data.TelegramStickers
import cc.sukazyo.cono.morny.util.tgapi.formatting.TGToString
import com.pengrad.telegrambot.model.request.ParseMode
import com.pengrad.telegrambot.model.{Chat, Message, Update, User}
import com.pengrad.telegrambot.request.{ForwardMessage, GetChat, SendMessage, SendSticker}
import scala.language.postfixOps
object OnCallMe extends EventListener {
private val me = MornyCoeur.config.trustedMaster
override def onMessage (update: Update): Boolean = {
if update.message.text == null then return false
if update.message.chat.`type` != (Chat.Type Private) then return false
(update.message.text toLowerCase) match
case "steam" | "sbeam" | "sdeam" =>
requestItem(update.message.from, "<b>STEAM LIBRARY</b>")
case "hana paresu" | "花宫" | "内群" =>
requestItem(update.message.from, "<b>Hana Paresu</b>")
case "dinner" | "lunch" | "breakfast" | "meal" | "eating" | "安妮今天吃什么" =>
requestLastDinner(update.message)
case cc if cc startsWith "cc::" =>
requestCustom(update.message)
case _ =>
return false
MornyCoeur.extra exec SendSticker(
update.message.chat.id,
TelegramStickers ID_SENT
).replyToMessageId(update.message.messageId)
true
}
private def requestItem (user: User, itemHTML: String, extra: String|Null = null): Unit =
MornyCoeur.extra exec SendMessage(
me,
s"""request $itemHTML
|from ${(TGToString as user) fullnameRefHtml}${if extra == null then "" else "\n"+extra}"""
.stripMargin
).parseMode(ParseMode HTML)
private def requestLastDinner (req: Message): Unit = {
var isAllowed = false
var lastDinnerData: Message|Null = null
if (MornyCoeur.trustedInstance isTrustedForDinnerRead req.from.id) {
lastDinnerData = (MornyCoeur.extra exec GetChat(MornyCoeur.config.dinnerChatId)).chat.pinnedMessage
val sendResp = MornyCoeur.extra exec ForwardMessage(
req.from.id,
lastDinnerData.forwardFromChat.id,
lastDinnerData.forwardFromMessageId
)
import cc.sukazyo.cono.morny.util.CommonFormat.{formatDate, formatDuration}
import cc.sukazyo.cono.morny.util.tgapi.formatting.MsgEscape.escapeHtml as h
def lastDinner_dateMillis: Long = lastDinnerData.forwardDate longValue;
MornyCoeur.extra exec SendMessage(
req.from.id,
"<i>on</i> <code>%s [UTC+8]</code>\n- <code>%s</code> <i>before</i>".formatted(
h(formatDate(lastDinner_dateMillis, 8)),
h(formatDuration(lastDinner_dateMillis))
)
).parseMode(ParseMode HTML).replyToMessageId(sendResp.message.messageId)
isAllowed = true
} else {
MornyCoeur.extra exec SendSticker(
req.from.id,
TelegramStickers ID_403
).replyToMessageId(req.messageId)
}
import Math.abs
requestItem(
req.from, "<b>Last Annie Dinner</b>",
if isAllowed then s"Allowed and returned https://t.me/c/${abs(lastDinnerData.forwardFromChat.id+1000000000000L)}/${lastDinnerData.forwardFromMessageId}"
else "Forbidden by perm check."
)
}
private def requestCustom (message: Message): Unit =
requestItem(message.from, "<u>[???]</u>")
MornyCoeur.extra exec ForwardMessage(me, message.chat.id, message.messageId)
}

View File

@ -0,0 +1,37 @@
package cc.sukazyo.cono.morny.bot.event
import cc.sukazyo.cono.morny.MornyCoeur
import cc.sukazyo.cono.morny.bot.api.{EventListener, InlineQueryUnit}
import com.pengrad.telegrambot.model.Update
import com.pengrad.telegrambot.model.request.InlineQueryResult
import com.pengrad.telegrambot.request.AnswerInlineQuery
import scala.collection.mutable.ListBuffer
import scala.language.postfixOps
import scala.reflect.ClassTag
object OnInlineQuery extends EventListener {
override def onInlineQuery (update: Update): Boolean = {
val results: List[InlineQueryUnit[_]] = MornyCoeur.queryManager query update
var cacheTime = Int.MaxValue
var isPersonal = InlineQueryUnit.DEFAULT_INLINE_PERSONAL_RESP
val resultAnswers = ListBuffer[InlineQueryResult[_]]()
for (r <- results) {
if (cacheTime > r.cacheTime) cacheTime = r.cacheTime
if (r isPersonal) isPersonal = true
resultAnswers += r.result
}
if (results isEmpty) return false
MornyCoeur.extra exec AnswerInlineQuery(
update.inlineQuery.id, resultAnswers toArray:_*
).cacheTime(cacheTime).isPersonal(isPersonal)
true
}
}

View File

@ -0,0 +1,38 @@
package cc.sukazyo.cono.morny.bot.event
import cc.sukazyo.cono.morny.MornyCoeur
import cc.sukazyo.cono.morny.bot.api.EventListener
import com.pengrad.telegrambot.model.Update
import com.pengrad.telegrambot.request.SendMessage
import scala.language.postfixOps
object OnUserRandom extends EventListener {
private val USER_OR_QUERY = "(.+)(?:还是|or)(.+)"r
private val USER_IF_QUERY = "(.+)[吗?|]+$"r
override def onMessage(update: Update): Boolean = {
if update.message.text == null then return false
if update.message.text startsWith "/" then return false
import cc.sukazyo.cono.morny.util.CommonRandom.iif
val query = update.message.text substring 1
val result: String|Null = query match
case USER_OR_QUERY(_con1, _con2) =>
if iif then _con1 else _con2
case USER_IF_QUERY(_con) =>
(if iif then "不" else "") + _con
case _ => null
if result == null then return false
MornyCoeur.extra exec SendMessage(
update.message.chat.id, result
).replyToMessageId(update.message.messageId)
true
}
}

View File

@ -0,0 +1,67 @@
package cc.sukazyo.cono.morny.bot.event
import cc.sukazyo.cono.morny.MornyCoeur
import cc.sukazyo.cono.morny.bot.api.EventListener
import cc.sukazyo.cono.morny.util.UniversalCommand
import cc.sukazyo.cono.morny.util.tgapi.formatting.MsgEscape.escapeHtml as h
import cc.sukazyo.cono.morny.util.tgapi.formatting.TGToString
import com.pengrad.telegrambot.model.Update
import com.pengrad.telegrambot.model.request.ParseMode
import com.pengrad.telegrambot.request.SendMessage
import scala.language.postfixOps
object OnUserSlashAction extends EventListener {
private val TG_FORMAT = "^\\w+(@\\w+)?$"r
override def onMessage (update: Update): Boolean = {
val text = update.message.text;
if text == null then return false
if (text startsWith "/") {
val actions = UniversalCommand format text
actions(0) = actions(0) substring 1
actions(0)
actions(0) match
case TG_FORMAT(_) =>
return false
case x if x contains "/" => return false
val isHardParse = actions(0) isBlank
def hp_len(i: Int) = if isHardParse then i+1 else i
if isHardParse && actions.length < 2 then return false
val v_verb = actions(hp_len(0))
val hasObject = actions.length != hp_len(1)
val v_object =
if hasObject then
actions slice(hp_len(1), actions.length) mkString(" ")
else ""
val origin = update.message
val target =
if update.message.replyToMessage == null then
origin
else update.message.replyToMessage
MornyCoeur.extra exec SendMessage(
update.message.chat.id,
"%s %s%s %s %s!".format(
(TGToString as origin) getSenderFirstNameRefHtml,
h(v_verb), if hasObject then "" else "了",
if (origin == target)
s"<a href='tg://user?id=${(TGToString as target) getSenderId}'>自己</a>"
else (TGToString as target) getSenderFirstNameRefHtml,
if hasObject then h(v_object+" ") else ""
)
).parseMode(ParseMode HTML).replyToMessageId(update.message.messageId)
true
} else false
}
}

View File

@ -0,0 +1,11 @@
package cc.sukazyo.cono.morny.bot.query
import javax.annotation.Nullable
import cc.sukazyo.cono.morny.bot.api.InlineQueryUnit
import com.pengrad.telegrambot.model.Update
trait ITelegramQuery {
def query (event: Update): List[InlineQueryUnit[_]] | Null
}

Some files were not shown because too many files have changed in this diff Show More