scala port stage2 (not tested)

This commit is contained in:
A.C.Sukazyo Eyre 2023-09-07 22:15:06 +08:00
parent aafdcc1fb2
commit 1a31a22cd9
Signed by: Eyre_S
GPG Key ID: C17CE40291207874
71 changed files with 1093 additions and 1433 deletions

View File

@ -99,8 +99,8 @@ scala {
compileJava {
sourceCompatibility '17'
targetCompatibility '17'
sourceCompatibility proj_java.getMajorVersion()
targetCompatibility proj_java.getMajorVersion()
options.encoding = proj_file_encoding.name()
@ -108,9 +108,14 @@ scala {
compileScala {
sourceCompatibility proj_java.getMajorVersion()
targetCompatibility proj_java.getMajorVersion()
options.encoding = proj_file_encoding.name()
scalaCompileOptions.encoding = proj_file_encoding.name()
// scalaCompileOptions.additionalParameters.add("-Yexplicit-nulls")
}
}

View File

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

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@ -5,6 +5,7 @@ import cc.sukazyo.messiva.log.LogLevel;
import cc.sukazyo.messiva.logger.Logger;
import cc.sukazyo.messiva.appender.ConsoleAppender;
import javax.annotation.Nonnull;
import java.io.PrintWriter;
import java.io.StringWriter;
@ -48,7 +49,7 @@ public class Log {
* @return {@link String} 格式的异常的堆栈报告信息.
* @see 1.0.0-alpha5
*/
public static String exceptionLog (Exception e) {
public static String exceptionLog (@Nonnull Throwable e) {
final StringWriter stackTrace = new StringWriter();
e.printStackTrace(new PrintWriter(stackTrace));
return stackTrace.toString();

View File

@ -1,8 +1,8 @@
package cc.sukazyo.cono.morny;
import cc.sukazyo.cono.morny.bot.api.OnUpdate;
import cc.sukazyo.cono.morny.bot.api.TelegramUpdatesListener$;
import cc.sukazyo.cono.morny.bot.command.MornyCommands;
import cc.sukazyo.cono.morny.bot.event.EventListeners;
import cc.sukazyo.cono.morny.bot.event.MornyEventListeners;
import cc.sukazyo.cono.morny.bot.query.MornyQueries;
import cc.sukazyo.cono.morny.daemon.MornyDaemons;
import cc.sukazyo.cono.morny.daemon.TrackerDataManager;
@ -31,8 +31,6 @@ public class MornyCoeur {
/** 当前 Morny 的{@link MornyTrusted 信任验证机}实例 */
private final MornyTrusted trusted;
/** 当前 Morny 的 telegram 命令管理器 */
private final MornyCommands commandManager = new MornyCommands();
private final MornyQueries queryManager = new MornyQueries();
/** morny 的 bot 账户 */
@ -127,12 +125,12 @@ public class MornyCoeur {
MornyDaemons.start();
logger.info("start telegram events listening");
EventListeners.registerAllListeners();
INSTANCE.account.setUpdatesListener(OnUpdate::onNormalUpdate);
MornyEventListeners.registerAllEvents();
INSTANCE.account.setUpdatesListener(TelegramUpdatesListener$.MODULE$);
if (config.commandLoginRefresh) {
logger.info("resetting telegram command list");
commandManager().automaticUpdateList();
MornyCommands.automaticTGListUpdate();
}
logger.info("Coeur start complete");
@ -156,7 +154,7 @@ public class MornyCoeur {
private void exitCleanup () {
MornyDaemons.stop();
if (config.commandLogoutClear) {
commandManager.automaticRemoveList();
MornyCommands.automaticTGListRemove();
}
}
@ -288,11 +286,6 @@ public class MornyCoeur {
return INSTANCE.trusted;
}
@Nonnull
public static MornyCommands commandManager () {
return INSTANCE.commandManager;
}
@Nonnull
public static MornyQueries queryManager () {
return INSTANCE.queryManager;

View File

@ -1,66 +0,0 @@
package cc.sukazyo.cono.morny.bot.api;
import com.pengrad.telegrambot.model.Update;
import javax.annotation.Nonnull;
@SuppressWarnings("unused")
public abstract class EventListener {
public boolean onMessage (@Nonnull Update update) {
return false;
}
public boolean onEditedMessage (@Nonnull Update update) {
return false;
}
public boolean onChannelPost (@Nonnull Update update) {
return false;
}
public boolean onEditedChannelPost (@Nonnull Update update) {
return false;
}
public boolean onInlineQuery (@Nonnull Update update) {
return false;
}
public boolean onChosenInlineResult (@Nonnull Update update) {
return false;
}
public boolean onCallbackQuery (@Nonnull Update update) {
return false;
}
public boolean onShippingQuery (@Nonnull Update update) {
return false;
}
public boolean onPreCheckoutQuery (@Nonnull Update update) {
return false;
}
public boolean onPoll (@Nonnull Update update) {
return false;
}
public boolean onPollAnswer (@Nonnull Update update) {
return false;
}
public boolean onMyChatMemberUpdated (@Nonnull Update update) {
return false;
}
public boolean onChatMemberUpdated (@Nonnull Update update) {
return false;
}
public boolean onChatJoinRequest (@Nonnull Update update) {
return false;
}
}

View File

@ -1,118 +0,0 @@
package cc.sukazyo.cono.morny.bot.api;
import cc.sukazyo.cono.morny.Log;
import cc.sukazyo.cono.morny.daemon.MornyReport;
import cc.sukazyo.cono.morny.util.tgapi.event.EventRuntimeException;
import com.google.gson.GsonBuilder;
import com.pengrad.telegrambot.model.Update;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import static cc.sukazyo.cono.morny.Log.logger;
public class EventListenerManager {
private static final List<EventListener> listeners = new ArrayList<>();
private static class EventPublisher extends Thread {
private final Function<EventListener, Boolean> exec;
public EventPublisher(@Nonnull Update update, @Nonnull Function<EventListener, Boolean> exec) {
this.setName("EVT"+update.updateId());
this.exec = exec;
}
@Override
public void run () {
for (EventListener x : listeners) {
try {
if (exec.apply(x)) return;
} catch (Exception e) {
final StringBuilder errorMessage = new StringBuilder();
errorMessage.append("Event throws unexpected exception:\n");
errorMessage.append(Log.exceptionLog(e).indent(4));
if (e instanceof EventRuntimeException.ActionFailed) {
errorMessage.append("\ntg-api action: response track: ");
errorMessage.append(new GsonBuilder().setPrettyPrinting().create().toJson(
((EventRuntimeException.ActionFailed)e).getResponse()
).indent(4)).append('\n');
}
logger.error(errorMessage.toString());
MornyReport.exception(e, "on event running");
}
}
}
}
public static void addListener (@Nonnull EventListener... listeners) {
EventListenerManager.listeners.addAll(Arrays.asList(listeners));
}
public static void publishMessageEvent (@Nonnull Update update) {
new EventPublisher(update, x -> x.onMessage(update)).start();
}
public static void publishEditedMessageEvent (@Nonnull Update update) {
new EventPublisher(update, x -> x.onEditedMessage(update)).start();
}
public static void publishChannelPostEvent (@Nonnull Update update) {
new EventPublisher(update, x -> x.onChannelPost(update)).start();
}
public static void publishEditedChannelPostEvent (@Nonnull Update update) {
new EventPublisher(update, x -> x.onEditedChannelPost(update)).start();
}
public static void publishInlineQueryEvent (@Nonnull Update update) {
new EventPublisher(update, x -> x.onInlineQuery(update)).start();
}
public static void publishChosenInlineResultEvent (@Nonnull Update update) {
new EventPublisher(update, x -> x.onChosenInlineResult(update)).start();
}
public static void publishCallbackQueryEvent (@Nonnull Update update) {
new EventPublisher(update, x -> x.onCallbackQuery(update)).start();
}
public static void publishShippingQueryEvent (@Nonnull Update update) {
new EventPublisher(update, x -> x.onShippingQuery(update)).start();
}
public static void publishPreCheckoutQueryEvent (@Nonnull Update update) {
new EventPublisher(update, x -> x.onPreCheckoutQuery(update)).start();
}
public static void publishPollEvent (@Nonnull Update update) {
new EventPublisher(update, x -> x.onPoll(update)).start();
}
public static void publishPollAnswerEvent (@Nonnull Update update) {
new EventPublisher(update, x -> x.onPollAnswer(update)).start();
}
public static void publishMyChatMemberUpdatedEvent (@Nonnull Update update) {
new EventPublisher(update, x -> x.onMyChatMemberUpdated(update)).start();
}
public static void publishChatMemberUpdatedEvent (@Nonnull Update update) {
new EventPublisher(update, x -> x.onChatMemberUpdated(update)).start();
}
public static void publishChatJoinRequestEvent (@Nonnull Update update) {
new EventPublisher(update, x -> x.onChatJoinRequest(update)).start();
}
}

View File

@ -1,36 +0,0 @@
package cc.sukazyo.cono.morny.bot.api;
import com.pengrad.telegrambot.model.request.InlineQueryResult;
public class InlineQueryUnit<T extends InlineQueryResult<T>> {
public static final int DEFAULT_INLINE_CACHE_TIME = 300;
public static final boolean DEFAULT_INLINE_PERSONAL_RESP = false;
private int cacheTime = DEFAULT_INLINE_CACHE_TIME;
private boolean isPersonal = DEFAULT_INLINE_PERSONAL_RESP;
public final T result;
public InlineQueryUnit (T result) {
this.result = result;
}
public int cacheTime () {
return cacheTime;
}
public InlineQueryUnit<T> cacheTime (int cacheTime) {
this.cacheTime = cacheTime;
return this;
}
public boolean isPersonal () {
return isPersonal;
}
public InlineQueryUnit<T> isPersonal (boolean isPersonal) {
this.isPersonal = isPersonal;
return this;
}
}

View File

@ -1,59 +0,0 @@
package cc.sukazyo.cono.morny.bot.api;
import com.pengrad.telegrambot.UpdatesListener;
import com.pengrad.telegrambot.model.Update;
import javax.annotation.Nonnull;
import java.util.List;
public class OnUpdate {
public static int onNormalUpdate (@Nonnull List<Update> updates) {
for (Update update : updates) {
if (update.message() != null) {
EventListenerManager.publishMessageEvent(update);
}
if (update.editedMessage() != null) {
EventListenerManager.publishEditedMessageEvent(update);
}
if (update.channelPost() != null) {
EventListenerManager.publishChannelPostEvent(update);
}
if (update.editedChannelPost() != null) {
EventListenerManager.publishEditedChannelPostEvent(update);
}
if (update.inlineQuery() != null) {
EventListenerManager.publishInlineQueryEvent(update);
}
if (update.chosenInlineResult() != null) {
EventListenerManager.publishChosenInlineResultEvent(update);
}
if (update.callbackQuery() != null) {
EventListenerManager.publishCallbackQueryEvent(update);
}
if (update.shippingQuery() != null) {
EventListenerManager.publishShippingQueryEvent(update);
}
if (update.preCheckoutQuery() != null) {
EventListenerManager.publishPreCheckoutQueryEvent(update);
}
if (update.poll() != null) {
EventListenerManager.publishPollEvent(update);
}
if (update.pollAnswer() != null) {
EventListenerManager.publishPollAnswerEvent(update);
}
if (update.myChatMember() != null) {
EventListenerManager.publishMyChatMemberUpdatedEvent(update);
}
if (update.chatMember() != null) {
EventListenerManager.publishChatMemberUpdatedEvent(update);
}
if (update.chatJoinRequest() != null) {
EventListenerManager.publishChatJoinRequestEvent(update);
}
}
return UpdatesListener.CONFIRMED_UPDATES_ALL;
}
}

View File

@ -1,19 +0,0 @@
package cc.sukazyo.cono.morny.bot.command;
import cc.sukazyo.cono.morny.util.tgapi.InputCommand;
import com.pengrad.telegrambot.model.Update;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public interface ISimpleCommand {
@Nonnull
String getName();
@Nullable
String[] getAliases();
void execute (@Nonnull InputCommand command, @Nonnull Update event);
}

View File

@ -1,12 +0,0 @@
package cc.sukazyo.cono.morny.bot.command;
import javax.annotation.Nonnull;
public interface ITelegramCommand extends ISimpleCommand {
@Nonnull
String getParamRule();
@Nonnull
String getDescription();
}

View File

@ -1,297 +0,0 @@
package cc.sukazyo.cono.morny.bot.command;
import cc.sukazyo.cono.morny.MornyCoeur;
import cc.sukazyo.cono.morny.bot.event.OnUniMeowTrigger;
import cc.sukazyo.cono.morny.daemon.MornyReport;
import cc.sukazyo.cono.morny.data.MornyJrrp;
import cc.sukazyo.cono.morny.data.TelegramStickers;
import cc.sukazyo.cono.morny.util.tgapi.InputCommand;
import cc.sukazyo.cono.morny.util.tgapi.formatting.TGToString;
import com.pengrad.telegrambot.model.BotCommand;
import com.pengrad.telegrambot.model.DeleteMyCommands;
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 com.pengrad.telegrambot.request.SetMyCommands;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.*;
import static cc.sukazyo.cono.morny.Log.logger;
import static cc.sukazyo.cono.morny.util.tgapi.formatting.MsgEscape.escapeHtml;
public class MornyCommands {
private final Map<String, ISimpleCommand> commands = new LinkedHashMap<>();
private void pushCommandTo (@Nonnull String name, @Nonnull ISimpleCommand instance) {
if (commands.containsKey(name)) {
logger.warn(String.format("""
Telegram command instance named "%s" already exists and will be override by another command instance
- current: %s
- new : %s""",
name,
commands.get(name).getClass().getName(),
instance.getClass().getName()
));
}
commands.put(name, instance);
}
public void register (@Nonnull ISimpleCommand... list) {
for (ISimpleCommand instance : list) {
final String[] aliases = instance.getAliases();
pushCommandTo(instance.getName(), instance);
if (aliases!=null) for (String alias : aliases) pushCommandTo(alias, instance);
}
}
@SuppressWarnings("NonAsciiCharacters")
public MornyCommands () {
register(
new ON(),
new Hello(), /* new {@link HelloOnStart}, */
MornyInformation$.MODULE$,
GetUsernameAndId$.MODULE$,
EventHack$.MODULE$,
Nbnhhsh$.MODULE$,
IP186Query.IP$.MODULE$,
IP186Query.Whois$.MODULE$,
Encryptor$.MODULE$,
new SaveData(),
MornyInfoOnHello$.MODULE$,
new Version(),
new MornyRuntime(),
new Jrrp(),
new Exit(), new ExitAlias()
);
// 特殊的命令
register(
Testing$.MODULE$,
DirectMsgClear$.MODULE$
);
// 统一注册这些奇怪的东西&.&
register(
私わね$.MODULE$,
喵呜.Progynova$.MODULE$
);
// special: 注册出于兼容使用的特别 event 的数据
OnUniMeowTrigger.register(
喵呜.抱抱$.MODULE$,
喵呜.揉揉$.MODULE$,
喵呜.蹭蹭$.MODULE$,
喵呜.贴贴$.MODULE$
);
}
public boolean execute (@Nonnull InputCommand command, @Nonnull Update event) {
if (commands.containsKey(command.getCommand())) {
commands.get(command.getCommand()).execute(command, event);
return true;
}
return nonCommandExecutable(event, command);
}
public void automaticUpdateList () {
BotCommand[] commandList = getCommandListTelegram();
automaticRemoveList();
MornyCoeur.extra().exec(new SetMyCommands(
commandList
));
logger.info("automatic updated telegram command list :\n" + commandListToString(commandList));
}
public void automaticRemoveList () {
MornyCoeur.extra().exec(new DeleteMyCommands());
logger.info("cleaned up command list.");
}
private String commandListToString (@Nonnull BotCommand[] list) {
StringBuilder builder = new StringBuilder();
for (BotCommand signal : list) {
builder.append(signal.command()).append(" - ").append(signal.description()).append("\n");
}
return builder.substring(0, builder.length()-1);
}
public BotCommand[] getCommandListTelegram () {
final List<BotCommand> telegramFormatListing = new ArrayList<>();
commands.forEach((regKey, command) -> {
if (command instanceof ITelegramCommand && regKey.equals(command.getName())) {
telegramFormatListing.add(formatTelegramCommandListLine(
command.getName(),
((ITelegramCommand)command).getParamRule(),
((ITelegramCommand)command).getDescription()
));
if (command.getAliases() != null) for (String alias : command.getAliases()) {
telegramFormatListing.add(formatTelegramCommandListLine(alias, "", ""));
}
}
});
return telegramFormatListing.toArray(BotCommand[]::new);
}
private BotCommand formatTelegramCommandListLine (@Nonnull String commandName, @Nonnull String paramRule, @Nonnull String intro) {
return new BotCommand(commandName, paramRule.isEmpty() ? (intro) : (paramRule+" - "+intro));
}
private boolean nonCommandExecutable (Update event, InputCommand command) {
if (command.getTarget() == null) return false; // 无法解析的命令转交事件链后代处理
else { // 无法解析的显式命令格式报错找不到命令
MornyCoeur.extra().exec(new SendSticker(
event.message().chat().id(),
TelegramStickers.ID_404
).replyToMessageId(event.message().messageId())
);
return true;
}
}
/// /// /// /// /// /// /// /// ///
///
/// Old Simple Command Block
///
private static class ON implements ITelegramCommand {
@Nonnull @Override public String getName () { return "o"; }
@Nullable
@Override public String[] getAliases () { return null; }
@Nonnull @Override public String getParamRule () { return ""; }
@Nonnull @Override public String getDescription () { return "检查是否在线"; }
@Override public void execute (@Nonnull InputCommand command, @Nonnull Update event) { onCommandOnExec(event); }
}
private static void onCommandOnExec (@Nonnull Update event) {
MornyCoeur.extra().exec(new SendSticker(
event.message().chat().id(),
TelegramStickers.ID_ONLINE_STATUS_RETURN
).replyToMessageId(event.message().messageId())
);
}
private static class Hello implements ITelegramCommand {
@Nonnull @Override public String getName () { return "hello"; }
@Nullable @Override public String[] getAliases () { return new String[]{"hi"}; }
@Nonnull @Override public String getParamRule () { return ""; }
@Nonnull @Override public String getDescription () { return "打招呼"; }
@Override public void execute (@Nonnull InputCommand command, @Nonnull Update event) { onCommandHelloExec(event); }
}
/**
* {@link Hello} on special command /start
* Deprecated due to new {@link MornyInfoOnHello}
*/
@Deprecated @SuppressWarnings("unused")
private static class HelloOnStart implements ISimpleCommand { @Nonnull @Override public String getName () { return "start"; }@Nullable @Override public String[] getAliases () { return new String[0]; }@Override public void execute (@Nonnull InputCommand command, @Nonnull Update event) { onCommandHelloExec(event); }}
private static void onCommandHelloExec (@Nonnull Update event) {
MornyCoeur.extra().exec(new SendSticker(
event.message().chat().id(),
TelegramStickers.ID_HELLO
).replyToMessageId(event.message().messageId())
);
}
private static class Exit implements ITelegramCommand {
@Nonnull @Override public String getName () { return "exit"; }
@Nullable @Override public String[] getAliases () { return new String[0]; }
@Nonnull @Override public String getParamRule () { return ""; }
@Nonnull @Override public String getDescription () { return "关闭 Bot (仅可信成员)"; }
@Override public void execute (@Nonnull InputCommand command, @Nonnull Update event) { onCommandExitExec(event); }
}
private static class ExitAlias implements ISimpleCommand {
@Nonnull @Override public String getName () { return "quit"; }
@Nullable @Override public String[] getAliases () { return new String[]{"stop"}; }
@Override public void execute (@Nonnull InputCommand command, @Nonnull Update event) { onCommandExitExec(event); }
}
private static void onCommandExitExec (@Nonnull Update event) {
if (MornyCoeur.trustedInstance().isTrusted(event.message().from().id())) {
MornyCoeur.extra().exec(new SendSticker(
event.message().chat().id(),
TelegramStickers.ID_EXIT
).replyToMessageId(event.message().messageId())
);
logger.info("Morny exited by user " + TGToString.as(event.message().from()).toStringLogTag());
MornyCoeur.exit(0, event.message().from());
} else {
MornyCoeur.extra().exec(new SendSticker(
event.message().chat().id(),
TelegramStickers.ID_403
).replyToMessageId(event.message().messageId())
);
logger.info("403 exited tag from user " + TGToString.as(event.message().from()).toStringLogTag());
MornyReport.unauthenticatedAction("/exit", event.message().from());
}
}
private static class Version implements ISimpleCommand {
@Nonnull @Override public String getName () { return "version"; }
@Nullable @Override public String[] getAliases () { return null; }
@Nonnull @Deprecated public String getParamRule () { return ""; }
@Nonnull @Deprecated public String getDescription () { return "检查 Bot 版本信息"; }
@Override public void execute (@Nonnull InputCommand command, @Nonnull Update event) { MornyInformation.echoVersion(event); }
}
private static class MornyRuntime implements ISimpleCommand {
@Nonnull @Override public String getName () { return "runtime"; }
@Nullable @Override public String[] getAliases () { return null; }
@Nonnull @Deprecated public String getParamRule () { return ""; }
@Nonnull @Deprecated public String getDescription () { return "获取 Bot 运行时信息(包括版本号)"; }
@Override public void execute (@Nonnull InputCommand command, @Nonnull Update event) { MornyInformation.echoRuntime(event); }
}
private static class Jrrp implements ITelegramCommand {
@Nonnull @Override public String getName () { return "jrrp"; }
@Nullable @Override public String[] getAliases () { return null; }
@Nonnull @Override public String getParamRule () { return ""; }
@Nonnull @Override public String getDescription () { return "获取 (假的) jrrp"; }
@Override public void execute (@Nonnull InputCommand command, @Nonnull Update event) { onCommandJrrpExec(event); }
}
private static void onCommandJrrpExec (Update event) {
final double jrrp = MornyJrrp.getJrrpFromTelegramUser(event.message().from(), System.currentTimeMillis());
final String endChar = jrrp>70 ? "!" : jrrp>30 ? ";" : "...";
MornyCoeur.extra().exec(new SendMessage(
event.message().chat().id(),
String.format(
"%s 在(utc的)今天的运气指数是———— <code>%.2f%%</code> %s",
TGToString.as(event.message().from()).fullnameRefHtml(),
jrrp, escapeHtml(endChar)
)
).replyToMessageId(event.message().messageId()).parseMode(ParseMode.HTML));
}
private static class SaveData implements ITelegramCommand {
@Nonnull @Override public String getName () { return "save"; }
@Nullable @Override public String[] getAliases () { return null; }
@Nonnull @Override public String getParamRule () { return ""; }
@Nonnull @Override public String getDescription () { return "保存缓存数据到文件(仅可信成员)"; }
@Override public void execute (@Nonnull InputCommand command, @Nonnull Update event) { onSaveDataExec(event); }
}
/**
* @since 0.4.3.0
*/
private static void onSaveDataExec (Update event) {
if (MornyCoeur.trustedInstance().isTrusted(event.message().from().id())) {
logger.info("called save from command by " + TGToString.as(event.message().from()).toStringLogTag());
MornyCoeur.callSaveData();
MornyCoeur.extra().exec(new SendSticker(
event.message().chat().id(),
TelegramStickers.ID_SAVED
).replyToMessageId(event.message().messageId())
);
} else {
MornyCoeur.extra().exec(new SendSticker(
event.message().chat().id(),
TelegramStickers.ID_403
).replyToMessageId(event.message().messageId())
);
logger.info("403 call save tag from user " + TGToString.as(event.message().from()).toStringLogTag());
MornyReport.unauthenticatedAction("/save", event.message().from());
}
}
}

View File

@ -1,39 +0,0 @@
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 OnUpdateTimestampOffsetLock UPDATE_TIMESTAMP_OFFSET_LOCK = new OnUpdateTimestampOffsetLock();
public static final OnEventHackHandle EVENT_HACK_HANDLE = new OnEventHackHandle();
// static final OnKuohuanhuanNeedSleep KUOHUANHUAN_NEED_SLEEP = new OnKuohuanhuanNeedSleep();
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();
public static final OnUniMeowTrigger UNI_MEOW_TRIGGER = new OnUniMeowTrigger();
public static final OnQuestionMarkReply QUESTION_MARK_REPLY = new OnQuestionMarkReply();
public static void registerAllListeners () {
EventListenerManager.addListener(
// ACTIVITY_RECORDER,
UPDATE_TIMESTAMP_OFFSET_LOCK,
/* write functional event behind here */
// KUOHUANHUAN_NEED_SLEEP,
COMMANDS_LISTENER,
UNI_MEOW_TRIGGER,
RANDOMLY_TRIGGERED,
OnUserRandom$.MODULE$,
QUESTION_MARK_REPLY,
OnUserSlashAction$.MODULE$,
OnInlineQuery$.MODULE$,
OnCallMe$.MODULE$,
CALL_MSG_SEND,
MEDICATION_NOTIFY_APPLY,
EVENT_HACK_HANDLE
);
}
}

View File

@ -8,7 +8,7 @@ import com.pengrad.telegrambot.model.Update;
import javax.annotation.Nonnull;
@Deprecated
public class OnActivityRecord extends EventListener {
public class OnActivityRecord implements EventListener {
@Override
public boolean onMessage (@Nonnull Update update) {
@ -22,7 +22,7 @@ public class OnActivityRecord extends EventListener {
(long)update.message().date() * 1000
);
}
return super.onMessage(update);
return false;
}
}

View File

@ -1,222 +0,0 @@
package cc.sukazyo.cono.morny.bot.event;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import com.pengrad.telegrambot.model.Chat;
import com.pengrad.telegrambot.model.Message;
import com.pengrad.telegrambot.model.MessageEntity;
import com.pengrad.telegrambot.model.Update;
import com.pengrad.telegrambot.model.request.ParseMode;
import com.pengrad.telegrambot.request.GetChat;
import com.pengrad.telegrambot.request.SendMessage;
import com.pengrad.telegrambot.request.SendSticker;
import cc.sukazyo.cono.morny.MornyCoeur;
import cc.sukazyo.cono.morny.bot.api.EventListener;
import cc.sukazyo.cono.morny.data.TelegramStickers;
import com.pengrad.telegrambot.response.GetChatResponse;
import com.pengrad.telegrambot.response.SendResponse;
import static cc.sukazyo.cono.morny.util.tgapi.formatting.MsgEscape.escapeHtml;
public class OnCallMsgSend extends EventListener {
private static final Pattern REGEX_MSG_SENDREQ_DATA_HEAD = Pattern.compile("^\\*msg(-?\\d+)(\\*\\S+)?(?:\\n([\\s\\S]+))?$");
private record MessageToSend (
@Nullable String message,
@Nullable MessageEntity[] entities,
@Nullable ParseMode parseMode,
long targetId
) { }
@Override
public boolean onMessage(@Nonnull Update update) {
// 执行体检查
if (update.message().chat().type() != Chat.Type.Private) return false;
if (update.message().text() == null) return false;
if (!update.message().text().startsWith("*msg")) return false;
// 权限检查
if (!MornyCoeur.trustedInstance().isTrusted(update.message().from().id())) {
MornyCoeur.extra().exec(new SendSticker(
update.message().chat().id(),
TelegramStickers.ID_403
).replyToMessageId(update.message().messageId()));
return true;
}
Message msgsendReqRaw; // 用户书写的发送请求原文
MessageToSend msgsendReqBody; // 解析后的发送请求实例
// *msgsend 发送标识
// 处理发送要求
if (update.message().text().equals("*msgsend")) {
// 发送体处理
if (update.message().replyToMessage() == null) return answer404(update);
msgsendReqBody = parseRequest(update.message().replyToMessage());
if (msgsendReqBody == null || msgsendReqBody.message == null) return answer404(update);
// 执行发送任务
SendResponse sendResponse = MornyCoeur.getAccount().execute(parseMessageToSend(msgsendReqBody));
if (!sendResponse.isOk()) { // 发送失败
MornyCoeur.extra().exec(new SendMessage(
update.message().chat().id(),
String.format("""
<b><u>%d</u> FAILED</b>
<code>%s</code>""",
sendResponse.errorCode(),
sendResponse.description()
)
).replyToMessageId(update.message().messageId()).parseMode(ParseMode.HTML));
} else { // 发送成功信号
MornyCoeur.extra().exec(new SendSticker(
update.message().chat().id(),
TelegramStickers.ID_SENT
).replyToMessageId(update.message().messageId()));
}
return true;
// 发送完成/失败 - 事件结束
}
// *msg 检查标识
if (update.message().text().equals("*msg")) { // 处理对曾经的原文的检查
if (update.message().replyToMessage() == null) {
return answer404(update);
}
msgsendReqRaw = update.message().replyToMessage();
} else if (update.message().text().startsWith("*msg")) { // 对接受到的原文进行检查
msgsendReqRaw = update.message();
} else {
return answer404(update); // 未定义的动作
}
// 对发送请求的用户原文进行解析
msgsendReqBody = parseRequest(msgsendReqRaw);
if (msgsendReqBody == null) {
return answer404(update);
}
// 输出发送目标信息
GetChatResponse targetChatReq = MornyCoeur.getAccount().execute(new GetChat(msgsendReqBody.targetId()));
if (!targetChatReq.isOk()) {
MornyCoeur.extra().exec(new SendMessage(
update.message().chat().id(),
String.format("""
<b><u>%d</u> FAILED</b>
<code>%s</code>""",
targetChatReq.errorCode(),
targetChatReq.description()
)
).replyToMessageId(update.message().messageId()).parseMode(ParseMode.HTML));
} else {
MornyCoeur.extra().exec(new SendMessage(
update.message().chat().id(),
targetChatReq.chat().type() == Chat.Type.Private ? (
String.format("""
<i><u>%d</u>@%s</i>
🔒 <b>%s</b> %s""",
msgsendReqBody.targetId(),
escapeHtml(targetChatReq.chat().type().name()),
escapeHtml(targetChatReq.chat().firstName()+(targetChatReq.chat().lastName()==null?"":" "+targetChatReq.chat().lastName())),
targetChatReq.chat().username()==null?
String.format("<a href='tg://user?id=%d'>@@</a>", targetChatReq.chat().id()):
(escapeHtml("@"+targetChatReq.chat().username()))
)
) : (
String.format("""
<i><u>%d</u>@%s</i>:::
%s <b>%s</b>%s""",
msgsendReqBody.targetId(),
escapeHtml(targetChatReq.chat().type().name()),
switch (targetChatReq.chat().type()) {
case group -> "💭";
case channel -> "📢";
case supergroup -> "💬";
default -> "⭕️";
},
escapeHtml(targetChatReq.chat().title()),
targetChatReq.chat().username() != null?String.format(
" @%s", escapeHtml(targetChatReq.chat().username())
):""
)
)
).replyToMessageId(update.message().messageId()).parseMode(ParseMode.HTML));
}
// 发送文本测试
if (msgsendReqBody.message == null) return true;
final SendResponse testSendResp = MornyCoeur.getAccount().execute(
parseMessageToSend(msgsendReqBody, update.message().chat().id()).replyToMessageId(update.message().messageId())
);
if (!testSendResp.isOk()) {
MornyCoeur.extra().exec(new SendMessage(
update.message().chat().id(),
String.format("""
<b><u>%d</u> FAILED</b>
<code>%s</code>""",
testSendResp.errorCode(),
testSendResp.description()
)
).replyToMessageId(update.message().messageId()).parseMode(ParseMode.HTML));
}
return true;
}
@Nullable
private static MessageToSend parseRequest (@Nonnull Message requestBody) {
final Matcher matcher = REGEX_MSG_SENDREQ_DATA_HEAD.matcher(requestBody.text());
if (matcher.matches()) {
long targetId = Long.parseLong(matcher.group(1));
ParseMode parseMode = matcher.group(2) == null ? null : switch (matcher.group(2)) {
case "*markdown", "*md", "*m↓" -> ParseMode.MarkdownV2;
case "*md1" -> ParseMode.Markdown;
case "*html" -> ParseMode.HTML;
default -> null;
};
final int offset = "*msg".length()+matcher.group(1).length()+(matcher.group(2)==null?0:matcher.group(2).length())+1;
final ArrayList<MessageEntity> entities = new ArrayList<>();
if (requestBody.entities() != null) for (MessageEntity entity : requestBody.entities()) {
final MessageEntity parsed = new MessageEntity(entity.type(), entity.offset() - offset, entity.length());
if (entity.url() != null) parsed.url(entity.url());
if (entity.user() != null) parsed.user(entity.user());
if (entity.language() != null) parsed.language(entity.language());
entities.add(parsed);
}
return new MessageToSend(matcher.group(3), entities.toArray(MessageEntity[]::new), parseMode, targetId);
}
return null;
}
@Nonnull
private static SendMessage parseMessageToSend (@Nonnull MessageToSend body) {
return parseMessageToSend(body, body.targetId);
}
@Nonnull
private static SendMessage parseMessageToSend (@Nonnull MessageToSend body, long targetId) {
SendMessage sendingBody = new SendMessage(targetId, body.message);
if (body.entities != null) sendingBody.entities(body.entities);
if (body.parseMode != null) sendingBody.parseMode(body.parseMode);
return sendingBody;
}
private static boolean answer404 (@Nonnull Update update) {
MornyCoeur.extra().exec(new SendSticker(
update.message().chat().id(),
TelegramStickers.ID_404
).replyToMessageId(update.message().messageId()));
return true;
}
}

View File

@ -1,145 +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.tgapi.formatting.MsgEscape;
import com.google.gson.GsonBuilder;
import com.pengrad.telegrambot.model.Update;
import com.pengrad.telegrambot.model.request.ParseMode;
import com.pengrad.telegrambot.request.SendMessage;
import javax.annotation.Nonnull;
import java.util.HashMap;
import java.util.Map;
import static cc.sukazyo.cono.morny.Log.logger;
/**
* 事件劫持与序列化工具.
* @since 0.4.2.0
*/
public class OnEventHackHandle extends EventListener {
/** 事件劫持请求列表 */
private static final Map<String, Hacker> hackers = new HashMap<>();
/**
* 触发事件劫持的限定条件.
* @since 0.4.2.0
*/
public enum HackType {
/** 只有相同用户发起的事件才会被触发 */
USER,
/** 只有相同群组内发生的事件才会触发 */
GROUP,
/** 任何事件都可以触发 */
ANY
}
public record Hacker(long fromChatId, long fromMessageId) {
@Override public String toString() {
return fromChatId + "/" + fromMessageId;
}
}
/**
* @since 0.4.2.0
*/
public static void registerHack(long fromMessageId, long fromUserId, long fromChatId, @Nonnull HackType type) {
String rec = null;
switch (type) {
case USER -> rec = String.format("((%d))", fromUserId);
case GROUP -> rec = String.format("{{%d}}", fromChatId);
case ANY -> rec = "[[]]";
}
hackers.put(rec, new Hacker(fromChatId, fromMessageId));
logger.debug("add hacker track " + rec);
}
private boolean onEventHacked (Update update, long chatId, long fromUser) {
logger.debug(String.format("got event signed {{%d}}((%d))", chatId, fromUser));
Hacker x;
x = hackers.remove(String.format("((%d))", fromUser));
if (x == null) x = hackers.remove(String.format("{{%d}}", chatId));
if (x == null) x = hackers.remove("[[]]");
if (x == null) return false;
logger.debug("hacked event by " + x);
MornyCoeur.extra().exec(new SendMessage(x.fromChatId, String.format(
"<code>%s</code>",
MsgEscape.escapeHtml(new GsonBuilder().setPrettyPrinting().create().toJson(update))
)).parseMode(ParseMode.HTML).replyToMessageId((int)x.fromMessageId));
return true;
}
@Override
public boolean onMessage (@Nonnull Update update) {
return onEventHacked(update, update.message().chat().id(), update.message().from().id());
}
@Override
public boolean onEditedMessage (@Nonnull Update update) {
return onEventHacked(update, update.editedMessage().chat().id(), update.editedMessage().from().id());
}
@Override
public boolean onChannelPost (@Nonnull Update update) {
return onEventHacked(update, update.channelPost().chat().id(), update.channelPost().chat().id());
}
@Override
public boolean onEditedChannelPost (@Nonnull Update update) {
return onEventHacked(update, update.editedChannelPost().chat().id(), update.editedChannelPost().chat().id());
}
@Override
public boolean onInlineQuery (@Nonnull Update update) {
return onEventHacked(update, 0, update.inlineQuery().from().id());
}
@Override
public boolean onChosenInlineResult (@Nonnull Update update) {
return onEventHacked(update, 0, update.chosenInlineResult().from().id());
}
@Override
public boolean onCallbackQuery (@Nonnull Update update) {
return onEventHacked(update, 0, update.callbackQuery().from().id());
}
@Override
public boolean onShippingQuery (@Nonnull Update update) {
return onEventHacked(update, 0, update.shippingQuery().from().id());
}
@Override
public boolean onPreCheckoutQuery (@Nonnull Update update) {
return onEventHacked(update, 0, update.preCheckoutQuery().from().id());
}
@Override
public boolean onPoll (@Nonnull Update update) {
return onEventHacked(update, 0, 0);
}
@Override
public boolean onPollAnswer (@Nonnull Update update) {
return onEventHacked(update, 0, update.pollAnswer().user().id());
}
@Override
public boolean onMyChatMemberUpdated (@Nonnull Update update) {
return onEventHacked(update, update.myChatMember().chat().id(), update.myChatMember().from().id());
}
@Override
public boolean onChatMemberUpdated (@Nonnull Update update) {
return onEventHacked(update, update.chatMember().chat().id(), update.chatMember().from().id());
}
@Override
public boolean onChatJoinRequest (@Nonnull Update update) {
return onEventHacked(update, update.chatJoinRequest().chat().id(), update.chatJoinRequest().from().id());
}
}

View File

@ -11,7 +11,7 @@ import java.util.GregorianCalendar;
import java.util.Locale;
@Deprecated
public class OnKuohuanhuanNeedSleep extends EventListener {
public class OnKuohuanhuanNeedSleep implements EventListener {
@Override
public boolean onMessage (@Nonnull Update update) {

View File

@ -1,29 +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.daemon.MornyDaemons;
import com.pengrad.telegrambot.model.Message;
import com.pengrad.telegrambot.model.Update;
import javax.annotation.Nonnull;
public class OnMedicationNotifyApply extends EventListener {
@Override
public boolean onEditedChannelPost (@Nonnull Update update) {
return editedMessageProcess(update.editedChannelPost());
}
@Override
public boolean onEditedMessage (@Nonnull Update update) {
return editedMessageProcess(update.editedMessage());
}
private boolean editedMessageProcess (Message edited) {
if (edited.chat().id() != MornyCoeur.config().medicationNotifyToChat) return false;
MornyDaemons.medicationTimerInstance.refreshNotificationWrite(edited);
return true;
}
}

View File

@ -1,38 +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.Set;
import static cc.sukazyo.cono.morny.util.CommonRandom.probabilityTrue;
public class OnQuestionMarkReply extends EventListener {
/**
* 一个 unicode 的问号字符列表. 不仅有半角全角问号也包含了变体问号和叹号结合的问好以及 uni-emoji 问号
* @since 1.0.0-RC3.2
*/
public static final Set<Character> QUESTION_MARKS = Set.of('?', '', '¿', '⁈', '⁇', '‽', '❔', '❓');
@Override
public boolean onMessage (@Nonnull Update update) {
if (update.message().text() == null) return false;
if (!probabilityTrue(8)) return false;
for (char c : update.message().text().toCharArray()) {
if (!QUESTION_MARKS.contains(c)) return false;
}
MornyCoeur.extra().exec(new SendMessage(
update.message().chat().id(), update.message().text()
).replyToMessageId(update.message().messageId()));
return true;
}
}

View File

@ -2,7 +2,8 @@ package cc.sukazyo.cono.morny.bot.event;
import cc.sukazyo.cono.morny.bot.api.EventListener;
public class OnRandomlyTriggered extends EventListener {
@Deprecated
public class OnRandomlyTriggered implements EventListener {
// /**
// * function CODE_IK0XA1

View File

@ -1,30 +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.tgapi.InputCommand;
import com.pengrad.telegrambot.model.Update;
import javax.annotation.Nonnull;
import static cc.sukazyo.cono.morny.Log.logger;
public class OnTelegramCommand extends EventListener {
@Override
public boolean onMessage (@Nonnull Update event) {
if (event.message().text() == null || !event.message().text().startsWith("/") || event.message().text().startsWith("/ ")) {
logger.debug("not command");
return false; // 检测到非(命令格式)文本忽略掉命令处理
}
final InputCommand command = new InputCommand(event.message().text().substring(1));
if (!command.getCommand().matches("^\\w+$")) { logger.debug("not command");return false; }
logger.debug("is command");
if (command.getTarget() != null && !MornyCoeur.getUsername().equals(command.getTarget())) {
return true; // 检测到命令并非针对 morny退出整个事件处理链
}
return MornyCoeur.commandManager().execute(command, event); // 转交命令管理器执行命令
}
}

View File

@ -1,36 +0,0 @@
package cc.sukazyo.cono.morny.bot.event;
import cc.sukazyo.cono.morny.bot.api.EventListener;
import cc.sukazyo.cono.morny.bot.command.ISimpleCommand;
import cc.sukazyo.cono.morny.util.tgapi.InputCommand;
import com.pengrad.telegrambot.model.Update;
import javax.annotation.Nonnull;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
public class OnUniMeowTrigger extends EventListener {
private static final Map<String, ISimpleCommand> triggers = new HashMap<>();
public static void register (ISimpleCommand... list) {
for (ISimpleCommand cmd : list)
triggers.put(cmd.getName(), cmd);
}
@Override
public boolean onMessage (@Nonnull Update event) {
if (event.message().text() == null) return false;
AtomicBoolean ok = new AtomicBoolean(false);
triggers.forEach((name, command) -> {
name = "/" + name;
if (name.equals(event.message().text())) {
command.execute(new InputCommand(name), event);
ok.set(true);
}
});
return ok.get();
}
}

View File

@ -1,56 +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 javax.annotation.Nonnull;
/**
* 阻止 {@link cc.sukazyo.cono.morny.MornyConfig#eventOutdatedTimestamp 指定时间} 之前的事件处理.
* <p>
* 只支持以下事件
* <ul>
* <li>{@link EventListener#onMessage(Update) 收到消息}</li>
* <li>{@link EventListener#onEditedMessage(Update) 消息被更新}</li>
* <li>{@link EventListener#onChannelPost(Update) 收到频道消息}</li>
* <li>{@link EventListener#onEditedChannelPost(Update) 频道消息被更新}</li>
* </ul>
* @see #isOutdated 时间判断
*/
public class OnUpdateTimestampOffsetLock extends EventListener {
/**
* 检查传入时间是否在要求时间之前"过期".
* @param timestamp 传入时间秒级
* @return 如果传入时间在要求时间<u>之前</u>返回true反之false
* @since 0.4.2.7
*/
public boolean isOutdated(long timestamp) {
return timestamp < MornyCoeur.config().eventOutdatedTimestamp/1000;
}
@Override
public boolean onMessage (@Nonnull Update update) {
return isOutdated(update.message().date());
}
/** @since 0.4.2.6 */
@Override
public boolean onEditedMessage (@Nonnull Update update) {
return isOutdated(update.editedMessage().editDate());
}
/** @since 0.4.2.6 */
@Override
public boolean onChannelPost (@Nonnull Update update) {
return isOutdated(update.channelPost().date());
}
/** @since 0.4.2.6 */
@Override
public boolean onEditedChannelPost (@Nonnull Update update) {
return isOutdated(update.editedChannelPost().editDate());
}
}

View File

@ -37,7 +37,7 @@ public class MornyReport {
}
}
public static void exception (@Nonnull Exception e, @Nullable String description) {
public static void exception (@Nonnull Throwable e, @Nullable String description) {
if (unsupported()) return;
executeReport(new SendMessage(
MornyCoeur.config().reportToChat,

View File

@ -1,46 +0,0 @@
package cc.sukazyo.cono.morny.data;
import cc.sukazyo.cono.morny.util.CommonConvert;
import cc.sukazyo.cono.morny.util.CommonEncrypt;
import com.pengrad.telegrambot.model.User;
/**
* Morny jrrp 运算类.
*
* @see #getJrrpFromTelegramUser(User,long)
* @see #calcJrrpXmomi(long,long)
* @since 0.4.2.9
*/
public class MornyJrrp {
/**
* 通过 telegram 用户和时间戳作为参数获取 jrrp.
*
* @see #calcJrrpXmomi 当前版本的实现算法 {@code Xmomi}
* @since 0.4.2.9
* @param user telegram 用户
* @param timestamp 时间戳
* @return 通过当前版本的算法计算出的用户 jrrp 取值为 {@code [0.00, 100.00]}
*/
public static double getJrrpFromTelegramUser (User user, long timestamp) {
return calcJrrpXmomi(user.id(), timestamp / (1000 * 60 * 60 * 24)) * 100.0;
}
/**
* {@code Xmomi} 版本的 jrrp 算法.
* <p>
* 算法规则为将用户id与日期戳链接为 <u><code>uid@daystamp</code></u> 这样的字符串
* 然后通过 MD5 计算出字符串的哈希值取哈希值前4个字节将其作为16进制数值表示法转换为取值为 {@code [0x0000, 0xffff]} 的数值
* 得到的数值除以区间最大值 {@code 0xffff} 即可得到一个分布在 {@code [0.0, 1.0]} 之间的分布值
* 这个分布值乘以 {@code 100.0}即为计算得到的 jrrp 数值
*
* @since 0.4.2.9
* @param userId telegram 用户 uid
* @param dayStamp unix 时间戳转换为日期单位后的数值. 数值应该在转换前转换时区
* @return 算法得到的 jrrp 取值为 {@code [0.00. 100.00]}
*/
public static double calcJrrpXmomi (long userId, long dayStamp) {
return (double)Long.parseLong(CommonConvert.byteArrayToHex(CommonEncrypt.hashMd5(userId + "@" + dayStamp)).substring(0, 4), 16) / (double)0xffff;
}
}

View File

@ -1,47 +0,0 @@
package cc.sukazyo.cono.morny.data;
import java.io.IOException;
import com.google.gson.Gson;
import cc.sukazyo.cono.morny.util.OkHttpPublic.MediaTypes;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
public class NbnhhshQuery {
public static class Word {
public String name;
public String[] trans;
public String[] inputting;
}
public static class GuessResult {
public Word[] words;
}
public record GuessReq (String text) {}
public static final String API_URL = "https://lab.magiconch.com/api/nbnhhsh/";
public static final String API_GUESS_METHOD = "guess/";
private static final OkHttpClient httpClient = new OkHttpClient();
public static GuessResult sendGuess (String text) throws IOException {
final String reqJsonText = new Gson().toJson(new GuessReq(text));
Request request = new Request.Builder()
.url(API_URL + API_GUESS_METHOD)
.post(RequestBody.create(reqJsonText, MediaTypes.JSON))
.build();
try (Response response = httpClient.newCall(request).execute()) {
final ResponseBody body = response.body();
if (body == null) throw new IOException("Null body.");
final String x = "{ \"words\": " + body.string() + " }";
return new Gson().fromJson(x, GuessResult.class);
}
}
}

View File

@ -1,10 +1,15 @@
package cc.sukazyo.cono.morny.util.tgapi.formatting;
import com.pengrad.telegrambot.model.Chat;
import com.pengrad.telegrambot.model.Message;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class TGToStringFromChat {
public static final long MASK_BOTAPI_ID = -1000000000000L;
private final Chat data;
public TGToStringFromChat(Chat chat) {
@ -20,4 +25,36 @@ public class TGToStringFromChat {
(String.format("%s {%s}[%d]", data.title(), data.username(), data.id()));
}
@Nonnull
public String getSafeName () {
if (data.type() == Chat.Type.Private)
return data.firstName() + (data.lastName()==null ? "" : " "+data.lastName());
else return data.title();
}
@Nullable
public String getSafeLinkHTML () {
if (data.username() == null) {
if (data.type() == Chat.Type.Private)
// language=html
return String.format("<a href='tg://user?id=%d'>@[u:%d]</a>", data.id(), data.id());
// language=html
else return String.format("<a href='https://t.me/c/%d'>@[c/%d]</a>", id_tdLib(), id_tdLib());
} else return "@"+data.username();
}
public long id_tdLib () {
return data.id() < 0 ? Math.abs(data.id() - MASK_BOTAPI_ID) : data.id();
}
@Nonnull
public String getTypeTag () {
return switch (data.type()) {
case Private -> "🔒";
case group -> "💭";
case supergroup -> "💬";
case channel -> "📢";
};
}
}

View File

@ -0,0 +1,22 @@
package cc.sukazyo.cono.morny.bot.api
import com.pengrad.telegrambot.model.Update
trait EventListener {
def onMessage (using Update): Boolean = false
def onEditedMessage (using Update): Boolean = false
def onChannelPost (using Update): Boolean = false
def onEditedChannelPost (using Update): Boolean = false
def onInlineQuery (using Update): Boolean = false
def onChosenInlineResult (using Update): Boolean = false
def onCallbackQuery (using Update): Boolean = false
def onShippingQuery (using Update): Boolean = false
def onPreCheckoutQuery (using Update): Boolean = false
def onPoll (using Update): Boolean = false
def onPollAnswer (using Update): Boolean = false
def onMyChatMemberUpdated (using Update): Boolean = false
def onChatMemberUpdated (using Update): Boolean = false
def onChatJoinRequest (using Update): Boolean = false
}

View File

@ -0,0 +1,84 @@
package cc.sukazyo.cono.morny.bot.api
import cc.sukazyo.cono.morny.Log
import cc.sukazyo.cono.morny.util.tgapi.event.EventRuntimeException
import cc.sukazyo.cono.morny.Log.{exceptionLog, logger}
import cc.sukazyo.cono.morny.daemon.MornyReport
import com.google.gson.GsonBuilder
import com.pengrad.telegrambot.model.Update
import scala.collection.mutable
import scala.language.postfixOps
object EventListenerManager {
private val listeners = mutable.Queue.empty[EventListener]
def register (listeners: EventListener*): Unit =
this.listeners ++= listeners
private class EventRunner (using event: Update) extends Thread {
this setName s"evt-${event.updateId()}-nn"
private def updateThreadName (t: String): Unit =
this setName s"evt-${event.updateId()}-$t"
override def run (): Unit = {
for (i <- listeners) {
object status:
var _status = 0
def isOk: Boolean = _status > 0
def check (u: Boolean): Unit = if u then _status = _status + 1
try {
updateThreadName("message")
if event.message ne null then status check i.onMessage
updateThreadName("edited-message")
if event.editedMessage ne null then status check i.onEditedMessage
updateThreadName("channel-post")
if event.channelPost ne null then status check i.onChannelPost
updateThreadName("edited-channel-post")
if event.editedChannelPost ne null then status check i.onEditedChannelPost
updateThreadName("inline-query")
if event.inlineQuery ne null then status check i.onInlineQuery
updateThreadName("chosen-inline-result")
if event.chosenInlineResult ne null then status check i.onChosenInlineResult
updateThreadName("callback-query")
if event.callbackQuery ne null then status check i.onCallbackQuery
updateThreadName("shipping-query")
if event.shippingQuery ne null then status check i.onShippingQuery
updateThreadName("pre-checkout-query")
if event.preCheckoutQuery ne null then status check i.onPreCheckoutQuery
updateThreadName("poll")
if event.poll ne null then status check i.onPoll
updateThreadName("poll-answer")
if event.pollAnswer ne null then status check i.onPollAnswer
updateThreadName("my-chat-member")
if event.myChatMember ne null then status check i.onMyChatMemberUpdated
updateThreadName("chat-member")
if event.chatMember ne null then status check i.onChatMemberUpdated
updateThreadName("chat-join-request")
if event.chatJoinRequest ne null then status check i.onChatJoinRequest
} catch case e => {
val errorMessage = StringBuilder()
errorMessage ++= "Event throws unexpected exception:\n"
errorMessage ++= (exceptionLog(e) indent 4)
e match
case actionFailed: EventRuntimeException.ActionFailed =>
errorMessage ++= "\ntg-api action: response track: "
errorMessage ++= (GsonBuilder().setPrettyPrinting().create().toJson(
actionFailed.getResponse
) indent 4) ++= "\n"
case _ =>
logger error errorMessage.toString
MornyReport.exception(e, "on event running")
}
if (status isOk) return
}
}
}
def publishUpdate (using Update): Unit = {
EventRunner().start()
}
}

View File

@ -0,0 +1,17 @@
package cc.sukazyo.cono.morny.bot.api
import com.pengrad.telegrambot.UpdatesListener
import com.pengrad.telegrambot.model.Update
import java.util
import scala.jdk.CollectionConverters.*
object TelegramUpdatesListener extends UpdatesListener {
override def process (updates: util.List[Update]): Int = {
for (update <- updates.asScala)
EventListenerManager.publishUpdate(using update)
UpdatesListener.CONFIRMED_UPDATES_ALL
}
}

View File

@ -11,10 +11,10 @@ import scala.language.postfixOps
object DirectMsgClear extends ISimpleCommand {
override def getName: String = "r"
override def getAliases: Array[String] = null
override val name: String = "r"
override val aliases: Array[ICommandAlias] | Null = null
override def execute (command: InputCommand, event: Update): Unit = {
override def execute (using command: InputCommand, event: Update): Unit = {
logger debug "executing command /r"
if (event.message.replyToMessage == null) return;

View File

@ -18,12 +18,12 @@ 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 val name: String = "encrypt"
override val aliases: Array[ICommandAlias] | Null = null
override val paramRule: String = "[algorithm|(l)] [(uppercase)]"
override val description: String = "通过指定算法加密回复的内容 (目前只支持文本)"
override def execute (command: InputCommand, event: Update): Unit = {
override def execute (using command: InputCommand, event: Update): Unit = {
val args = command.getArgs

View File

@ -11,12 +11,12 @@ 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 val name: String = "event_hack"
override val aliases: Array[ICommandAlias] | Null = null
override val paramRule: String = "[(user|group|any)]"
override val description: String = "输出 bot 下一个获取到的事件序列化数据"
override def execute (command: InputCommand, event: Update): Unit = {
override def execute (using command: InputCommand, event: Update): Unit = {
val x_mode = if (command.hasArgs) command.getArgs()(0) else ""

View File

@ -1,7 +1,8 @@
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 cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramUserInformation
import com.pengrad.telegrambot.model.Update
import com.pengrad.telegrambot.model.request.ParseMode
import com.pengrad.telegrambot.request.{GetChatMember, SendMessage}
@ -10,12 +11,12 @@ 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 val name: String = "user"
override val aliases: Array[ICommandAlias] | Null = null
override val paramRule: String = "[userid]"
override val description: String = "获取指定或回复的用户相关信息"
override def execute (command: InputCommand, event: Update): Unit = {
override def execute (using command: InputCommand, event: Update): Unit = {
val args = command.getArgs
@ -49,7 +50,7 @@ object GetUsernameAndId extends ITelegramCommand {
val user = response.chatMember.user
if (user.id eq Standardize.CHANNEL_SPEAKER_MAGIC_ID)
if (user.id == Standardize.CHANNEL_SPEAKER_MAGIC_ID)
MornyCoeur.extra exec SendMessage(
event.message.chat.id,
"<code>$__channel_identify</code>"

View File

@ -0,0 +1,18 @@
package cc.sukazyo.cono.morny.bot.command
trait ICommandAlias {
val name: String
val listed: Boolean
}
object ICommandAlias {
case class ListedAlias (name: String) extends ICommandAlias:
override val listed = true
case class HiddenAlias (name: String) extends ICommandAlias:
override val listed = false
}

View File

@ -16,19 +16,19 @@ object IP186Query {
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)
override val name: String = "ip"
override val aliases: Array[ICommandAlias]|Null = null
override val paramRule: String = "[ip]"
override val description: String = "通过 https://ip.186526.xyz 查询 ip 资料"
override def execute (using command: InputCommand, event: Update): Unit = query
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)
override val name: String = "whois"
override val aliases: Array[ICommandAlias]|Null = null
override val paramRule: String = "[domain]"
override val description: String = "通过 https://ip.186526.xyz 查询域名资料"
override def execute (using command: InputCommand, event: Update): Unit = query
private def query (event: Update, command: InputCommand): Unit = {
private def query (using event: Update, command: InputCommand): Unit = {
val target: String|Null =
if (command.getArgs isEmpty)

View File

@ -0,0 +1,13 @@
package cc.sukazyo.cono.morny.bot.command
import cc.sukazyo.cono.morny.util.tgapi.InputCommand
import com.pengrad.telegrambot.model.Update
trait ISimpleCommand {
val name: String
val aliases: Array[ICommandAlias]|Null
def execute (using command: InputCommand, event: Update): Unit
}

View File

@ -0,0 +1,8 @@
package cc.sukazyo.cono.morny.bot.command
trait ITelegramCommand extends ISimpleCommand {
val paramRule: String
val description: String
}

View File

@ -0,0 +1,111 @@
package cc.sukazyo.cono.morny.bot.command
import cc.sukazyo.cono.morny.util.tgapi.InputCommand
import cc.sukazyo.cono.morny.MornyCoeur
import cc.sukazyo.cono.morny.data.TelegramStickers
import com.pengrad.telegrambot.model.{BotCommand, DeleteMyCommands, Update}
import com.pengrad.telegrambot.request.{SendSticker, SetMyCommands}
import scala.collection.{mutable, SeqMap}
import scala.collection.mutable.ArrayBuffer
import scala.language.postfixOps
import cc.sukazyo.cono.morny.Log.logger
object MornyCommands {
private type CommandMap = SeqMap[String, ISimpleCommand]
private def CommandMap (commands: ISimpleCommand*): CommandMap =
val stash: mutable.SeqMap[String, ISimpleCommand] = mutable.SeqMap()
for (i <- commands) stash += ((i.name, i))
stash
private val commands: CommandMap = CommandMap(
MornyHellos.On,
MornyHellos.On,
MornyHellos.Hello,
MornyInfoOnStart,
GetUsernameAndId,
EventHack,
Nbnhhsh,
IP186Query.IP,
IP186Query.Whois,
Encryptor,
MornyManagers.SaveData,
MornyInformation,
MornyInformationOlds.Version,
MornyInformationOlds.Runtime,
MornyOldJrrp,
MornyManagers.Exit,
Testing,
DirectMsgClear,
私わね,
喵呜.Progynova
)
@SuppressWarnings(Array("NonAsciiCharacters"))
val commands_uni: CommandMap = CommandMap(
喵呜.抱抱,
喵呜.揉揉,
喵呜.贴贴,
喵呜.蹭蹭
)
def execute (using command: InputCommand, event: Update): Boolean = {
if (commands contains command.getCommand)
commands(command.getCommand) execute;
true
else nonCommandExecutable
}
private def nonCommandExecutable (using command: InputCommand, event: Update): Boolean = {
if command.getTarget eq null then false
else
MornyCoeur.extra exec SendSticker(
event.message.chat.id,
TelegramStickers ID_404
).replyToMessageId(event.message.messageId)
true
}
def automaticTGListUpdate (): Unit = {
val listing = commands_toTelegramList
automaticTGListRemove()
MornyCoeur.extra exec SetMyCommands(listing:_*)
logger info
s"""automatic updated telegram command list :
|${commandsTelegramList_toString(listing)}""".stripMargin
}
def automaticTGListRemove (): Unit = {
MornyCoeur.extra exec DeleteMyCommands()
logger info "cleaned up command list"
}
private def commandsTelegramList_toString (list: Array[BotCommand]): String =
val builder = StringBuilder()
for (single <- list)
builder ++= s"${single.command} - ${single.description}\n"
(builder dropRight 1) toString
private def commands_toTelegramList: Array[BotCommand] =
val list = ArrayBuffer.empty[BotCommand]
for ((name, command) <- commands) command match
case telegramCommand: ITelegramCommand if name == command.name =>
list ++= formatTelegramCommandListLine(telegramCommand)
case _ =>
list toArray
private def formatTelegramCommandListLine (command: ITelegramCommand): Array[BotCommand] =
def buildOne (name: String, paramRule: String, intro: String): BotCommand =
BotCommand(name, if paramRule isBlank then intro else s"$paramRule - $intro")
val list = mutable.ArrayBuffer[BotCommand](
buildOne(command.name, command.paramRule, command.description))
if (command.aliases ne null) for (alias <- command.aliases)
if (alias.listed) list += buildOne(alias.name, "", "↑")
list toArray
}

View File

@ -0,0 +1,43 @@
package cc.sukazyo.cono.morny.bot.command
import cc.sukazyo.cono.morny.util.tgapi.InputCommand
import cc.sukazyo.cono.morny.MornyCoeur
import cc.sukazyo.cono.morny.bot.command.ICommandAlias.ListedAlias
import cc.sukazyo.cono.morny.data.TelegramStickers
import com.pengrad.telegrambot.model.Update
import com.pengrad.telegrambot.request.SendSticker
import scala.language.postfixOps
object MornyHellos {
object On extends ITelegramCommand {
override val name: String = "on"
override val aliases: Array[ICommandAlias] | Null = null
override val paramRule: String = ""
override val description: String = "检查是否在线"
override def execute (using command: InputCommand, event: Update): Unit =
MornyCoeur.extra exec SendSticker(
event.message.chat.id,
TelegramStickers ID_ONLINE_STATUS_RETURN
).replyToMessageId(event.message.messageId)
}
object Hello extends ITelegramCommand {
override val name: String = "hello"
override val aliases: Array[ICommandAlias] | Null = Array(ListedAlias("hi"))
override val paramRule: String = ""
override val description: String = "打招呼"
override def execute (using command: InputCommand, event: Update): Unit =
MornyCoeur.extra exec SendSticker(
event.message.chat.id,
TelegramStickers ID_HELLO
).replyToMessageId(event.message.messageId)
}
}

View File

@ -8,12 +8,12 @@ import com.pengrad.telegrambot.request.SendPhoto
import scala.language.postfixOps
object MornyInfoOnHello extends ISimpleCommand {
object MornyInfoOnStart extends ISimpleCommand {
override def getName: String = "start"
override def getAliases: Array[String] = Array()
override val name: String = "start"
override val aliases: Array[ICommandAlias] | Null = null
override def execute (command: InputCommand, event: Update): Unit = {
override def execute (using command: InputCommand, event: Update): Unit = {
MornyCoeur.extra exec new SendPhoto(
event.message.chat.id,

View File

@ -23,12 +23,12 @@ object MornyInformation extends ITelegramCommand {
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 val name: String = "info"
override val aliases: Array[ICommandAlias]|Null = null
override val paramRule: String = "[(version|runtime|stickers[.IDs])]"
override val description: String = "输出当前 Morny 的各种信息"
override def execute (command: InputCommand, event: Update): Unit = {
override def execute (using command: InputCommand, event: Update): Unit = {
if (!command.hasArgs) {
echoInfo(event.message.chat.id, event.message.messageId)
@ -38,10 +38,10 @@ object MornyInformation extends ITelegramCommand {
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)
case Subs.STICKERS => echoStickers
case Subs.RUNTIME => echoRuntime
case Subs.VERSION | Subs.VERSION_2 => echoVersion
case _ => echo404
}
}
@ -92,11 +92,11 @@ object MornyInformation extends ITelegramCommand {
).parseMode(ParseMode HTML).replyToMessageId(replyTo)
}
private def echoStickers (command: InputCommand, event: Update): Unit = {
private def echoStickers (using 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()(0) == Subs.STICKERS) {
if (command.getArgs.length == 1) sid = ""
else if (command.getArgs.length == 2) sid = command.getArgs()(1)
} else if (command.getArgs.length == 1) {
@ -104,7 +104,7 @@ object MornyInformation extends ITelegramCommand {
sid = command.getArgs()(0) substring Subs.STICKERS.length+1
}
}
if (sid == null) echo404(event)
if (sid == null) echo404
else echoStickers(sid, chat, replyTo)
}
@ -113,11 +113,12 @@ object MornyInformation extends ITelegramCommand {
else TelegramStickers echoStickerByID(sid, MornyCoeur.extra, send_chat, send_replyTo)
}
private[command] def echoVersion (event: Update): Unit = {
private[command] def echoVersion (using 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,
// language=html
s"""version:
|- Morny <code>${h(MornySystem.CODENAME toUpperCase)}</code>
|- <code>${h(MornySystem.VERSION_BASE)}</code>$versionDeltaHTML${if (MornySystem.isGitBuild) "\n- " + versionGitHTML else ""}
@ -130,11 +131,11 @@ object MornyInformation extends ITelegramCommand {
).replyToMessageId(event.message.messageId).parseMode(ParseMode HTML)
}
private[command] def echoRuntime (event: Update): Unit = {
private[command] def echoRuntime (using event: Update): Unit = {
def sysprop (p: String): String = System.getProperty(p)
MornyCoeur.extra exec new SendMessage(
event.message.chat.id,
/* html */
/* language=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>
@ -158,7 +159,7 @@ object MornyInformation extends ITelegramCommand {
).parseMode(ParseMode HTML).replyToMessageId(event.message.messageId)
}
private def echo404 (event: Update): Unit =
private def echo404 (using event: Update): Unit =
MornyCoeur.extra exec new SendSticker(
event.message.chat.id,
TelegramStickers ID_404

View File

@ -0,0 +1,17 @@
package cc.sukazyo.cono.morny.bot.command
import cc.sukazyo.cono.morny.util.tgapi.InputCommand
import com.pengrad.telegrambot.model.Update
object MornyInformationOlds {
object Version extends ISimpleCommand:
override val name: String = "version"
override val aliases: Array[ICommandAlias] | Null = null
override def execute (using command: InputCommand, event: Update): Unit = MornyInformation.echoVersion
object Runtime extends ISimpleCommand:
override val name: String = "runtime"
override val aliases: Array[ICommandAlias] | Null = null
override def execute (using command: InputCommand, event: Update): Unit = MornyInformation.echoRuntime
}

View File

@ -0,0 +1,86 @@
package cc.sukazyo.cono.morny.bot.command
import cc.sukazyo.cono.morny.bot.command.ICommandAlias.HiddenAlias
import cc.sukazyo.cono.morny.util.tgapi.InputCommand
import cc.sukazyo.cono.morny.MornyCoeur
import cc.sukazyo.cono.morny.data.TelegramStickers
import cc.sukazyo.cono.morny.util.tgapi.formatting.TGToString
import com.pengrad.telegrambot.model.Update
import com.pengrad.telegrambot.request.SendSticker
import scala.language.postfixOps
import cc.sukazyo.cono.morny.Log.logger
import cc.sukazyo.cono.morny.daemon.MornyReport
object MornyManagers {
object Exit extends ITelegramCommand {
override val name: String = "exit"
override val aliases: Array[ICommandAlias] | Null = Array(HiddenAlias("stop"), HiddenAlias("quit"))
override val paramRule: String = "exit"
override val description: String = "关闭 Bot (仅可信成员)"
override def execute (using command: InputCommand, event: Update): Unit = {
val user = event.message.from
if (MornyCoeur.trustedInstance isTrusted user.id) {
MornyCoeur.extra exec SendSticker(
event.message.chat.id,
TelegramStickers ID_EXIT
).replyToMessageId(event.message.messageId)
logger info s"Morny exited by user ${(TGToString as user) toStringLogTag}"
MornyCoeur.exit(0, user)
} else {
MornyCoeur.extra exec SendSticker(
event.message.chat.id,
TelegramStickers ID_403
).replyToMessageId(event.message.messageId)
logger info s"403 exit caught from user ${(TGToString as user) toStringLogTag}"
MornyReport.unauthenticatedAction("/exit", user)
}
}
}
object SaveData extends ITelegramCommand {
override val name: String = "save"
override val aliases: Array[ICommandAlias] | Null = null
override val paramRule: String = ""
override val description: String = "保存缓存数据到文件(仅可信成员)"
override def execute (using command: InputCommand, event: Update): Unit = {
val user = event.message.from
if (MornyCoeur.trustedInstance isTrusted user.id) {
logger info s"call save from command by ${(TGToString as user) toStringLogTag}"
MornyCoeur.callSaveData()
MornyCoeur.extra exec SendSticker(
event.message.chat.id,
TelegramStickers ID_SAVED
).replyToMessageId(event.message.messageId)
} else {
MornyCoeur.extra exec SendSticker(
event.message.chat.id,
TelegramStickers ID_403
).replyToMessageId(event.message.messageId)
logger info s"403 save caught from user ${(TGToString as user) toStringLogTag}"
MornyReport.unauthenticatedAction("/save", user)
}
}
}
}

View File

@ -0,0 +1,36 @@
package cc.sukazyo.cono.morny.bot.command
import cc.sukazyo.cono.morny.util.tgapi.InputCommand
import com.pengrad.telegrambot.model.Update
import cc.sukazyo.cono.morny.data.MornyJrrp
import cc.sukazyo.cono.morny.MornyCoeur
import cc.sukazyo.cono.morny.util.tgapi.formatting.TGToString
import com.pengrad.telegrambot.model.request.ParseMode
import com.pengrad.telegrambot.request.SendMessage
import scala.language.postfixOps
object MornyOldJrrp extends ITelegramCommand {
override val name: String = "jrrp"
override val aliases: Array[ICommandAlias] | Null = null
override val paramRule: String = ""
override val description: String = "获取 (假的) jrrp"
override def execute (using command: InputCommand, event: Update): Unit = {
val user = event.message.from
val jrrp = MornyJrrp.jrrp_of_telegramUser(user, System.currentTimeMillis)
val ending = jrrp match
case s if s > 70 => "!"
case a if a > 30 => ";"
case _ => "..."
import cc.sukazyo.cono.morny.util.tgapi.formatting.MsgEscape.escapeHtml as h
MornyCoeur.extra exec SendMessage(
event.message.chat.id,
// language=html
f"${(TGToString as user) fullnameRefHtml} 在(utc的)今天的运气指数是———— <code>$jrrp%.2f%%</code>${h(ending)}"
).replyToMessageId(event.message.messageId).parseMode(ParseMode HTML)
}
}

View File

@ -13,14 +13,16 @@ 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>"
private val NBNHHSH_RESULT_HEAD_HTML =
// language=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 val name: String = "nbnhhsh"
override val aliases: Array[ICommandAlias]|Null = null
override val paramRule: String = "[text]"
override val description: String = "检索文本内 nbnhhsh 词条"
override def execute (command: InputCommand, event: Update): Unit = {
override def execute (using command: InputCommand, event: Update): Unit = {
val queryTarget: String|Null =
import cc.sukazyo.cono.morny.util.CommonConvert.stringsConnecting
@ -47,17 +49,17 @@ object Nbnhhsh extends ITelegramCommand {
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))
val _use_trans = (_word.trans ne null) && (_word.trans nonEmpty)
val _use_inputting = (_word.inputting ne null) && (_word.inputting nonEmpty)
if (_use_trans || _use_inputting)
message ++= s"\n\n<b>[[ ${h(_word.name)} ]]</b>"
logger debug s"**used [${_word.name}]"
if (_word.trans != null) for (_trans <- _word.trans)
if (_use_trans) for (_trans <- _word.trans)
message ++= s"\n* <i>${h(_trans)}</i>"
logger debug s"**used [${_word.name}] used `${_trans}``"
if (_word.inputting != null)
if (_use_inputting)
logger debug s"**used [${_word.name}] inputting"
if (_word.trans != null)
if (_use_trans)
message += '\n'
message ++= " maybe:"
for (_inputting <- _word.inputting)

View File

@ -12,17 +12,15 @@ import scala.language.postfixOps
object Testing extends ISimpleCommand {
override def getName: String = "test"
override def getAliases: Array[String] = null
override val name: String = "test"
override val aliases: Array[ICommandAlias] | Null = null
override def execute (command: InputCommand, event: Update): Unit = {
val a = StringBuilder("value")
a ++= "Changed"
override def execute (using command: InputCommand, event: Update): Unit = {
MornyCoeur.extra exec new SendMessage(
event.message.chat.id,
"<b>Just</b> a TEST command. num is:" + (a toString)
// language=html
"<b>Just</b> a TEST command."
).replyToMessageId(event.message.messageId).parseMode(ParseMode HTML)
}

View File

@ -15,39 +15,39 @@ import scala.language.postfixOps
object 喵呜 {
object 抱抱 extends ISimpleCommand {
override def getName: String = "抱抱"
override def getAliases: Array[String] = Array()
override def execute (command: InputCommand, event: Update): Unit =
replyingSet(event, "贴贴", "贴贴")
override val name: String = "抱抱"
override val aliases: Array[ICommandAlias]|Null = null
override def execute (using command: InputCommand, event: Update): Unit =
replyingSet("贴贴", "贴贴")
}
object 揉揉 extends ISimpleCommand {
override def getName: String = "揉揉"
override def getAliases: Array[String] = Array()
override def execute (command: InputCommand, event: Update): Unit =
replyingSet(event, "蹭蹭", "摸摸")
override val name: String = "揉揉"
override val aliases: Array[ICommandAlias]|Null = null
override def execute (using command: InputCommand, event: Update): Unit =
replyingSet("蹭蹭", "摸摸")
}
object 蹭蹭 extends ISimpleCommand {
override def getName: String = "蹭蹭"
override def getAliases: Array[String] = Array()
override def execute (command: InputCommand, event: Update): Unit =
replyingSet(event, "揉揉", "蹭蹭")
override val name: String = "蹭蹭"
override val aliases: Array[ICommandAlias]|Null = null
override def execute (using command: InputCommand, event: Update): Unit =
replyingSet("揉揉", "蹭蹭")
}
object 贴贴 extends ISimpleCommand {
override def getName: String = "贴贴"
override def getAliases: Array[String] = Array()
override def execute (command: InputCommand, event: Update): Unit =
replyingSet(event, "贴贴", "贴贴")
override val name: String = "贴贴"
override val aliases: Array[ICommandAlias]|Null = null
override def execute (using command: InputCommand, event: Update): Unit =
replyingSet("贴贴", "贴贴")
}
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 = {
override val name: String = "install"
override val aliases: Array[ICommandAlias]|Null = null
override val paramRule: String = ""
override val description: String = "抽取一个神秘盒子"
override def execute (using command: InputCommand, event: Update): Unit = {
MornyCoeur.extra exec new SendSticker(
event.message.chat.id,
TelegramStickers ID_PROGYNOVA
@ -55,7 +55,7 @@ object 喵呜 {
}
}
private def replyingSet (event: Update, whileRec: String, whileNew: String): Unit = {
private def replyingSet (whileRec: String, whileNew: String)(using event: Update): Unit = {
val isNew = event.message.replyToMessage == null;
val target = if (isNew) event.message else event.message.replyToMessage
MornyCoeur.extra exec new SendMessage(

View File

@ -8,10 +8,10 @@ import com.pengrad.telegrambot.request.SendMessage
object 私わね extends ISimpleCommand {
override def getName: String = "me"
override def getAliases: Array[String] = Array()
override val name: String = "me"
override val aliases: Array[ICommandAlias] | Null = null
override def execute (command: InputCommand, event: Update): Unit = {
override def execute (using command: InputCommand, event: Update): Unit = {
if (probabilityTrue(521)) {
val text = "/打假"

View File

@ -0,0 +1,27 @@
package cc.sukazyo.cono.morny.bot.event
import cc.sukazyo.cono.morny.bot.api.EventListenerManager
object MornyEventListeners {
def registerAllEvents(): Unit = {
EventListenerManager.register(
// ACTIVITY_RECORDER
OnUpdateTimestampOffsetLock,
// KUOHUANHUAN_NEED_SLEEP
OnTelegramCommand,
OnUniMeowTrigger,
OnUserRandom,
OnQuestionMarkReply,
OnUserSlashAction,
OnInlineQuery,
OnCallMe,
OnCallMsgSend,
OnMedicationNotifyApply,
OnEventHackHandle
)
}
}

View File

@ -15,7 +15,7 @@ object OnCallMe extends EventListener {
private val me = MornyCoeur.config.trustedMaster
override def onMessage (update: Update): Boolean = {
override def onMessage (using update: Update): Boolean = {
if update.message.text == null then return false
if update.message.chat.`type` != (Chat.Type Private) then return false

View File

@ -0,0 +1,153 @@
package cc.sukazyo.cono.morny.bot.event
import cc.sukazyo.cono.morny.bot.api.EventListener
import cc.sukazyo.cono.morny.MornyCoeur
import cc.sukazyo.cono.morny.data.TelegramStickers
import cc.sukazyo.cono.morny.util.tgapi.formatting.TGToString
import com.pengrad.telegrambot.model.{Chat, Message, MessageEntity, Update}
import com.pengrad.telegrambot.model.request.ParseMode
import com.pengrad.telegrambot.request.{GetChat, SendMessage, SendSticker}
import scala.collection.mutable.ArrayBuffer
import scala.language.postfixOps
import scala.util.matching.Regex
object OnCallMsgSend extends EventListener {
private val REGEX_MSG_SENDREQ_DATA_HEAD: Regex = "^\\*msg(-?\\d+)(\\*\\S+)?(?:\\n([\\s\\S]+))?$"r
case class MessageToSend (
message: String|Null,
entities: Array[MessageEntity]|Null,
parseMode: ParseMode|Null,
targetId: Long
) {
def toSendMessage (target_override: Long|Null = null): SendMessage =
val useTarget = if target_override == null then targetId else target_override
val sendMessage = SendMessage(useTarget, message)
if entities ne null then sendMessage.entities(entities:_*)
if parseMode ne null then sendMessage.parseMode(parseMode)
sendMessage
}
private object MessageToSend:
def from (raw: Message): MessageToSend = {
raw.text match
case REGEX_MSG_SENDREQ_DATA_HEAD(_target, _parseMode, _body) =>
val target = _target toLong
val parseMode: ParseMode | Null = _parseMode match
case "*markdown" | "*md" | "*m↓" => ParseMode MarkdownV2
case "*md1" => ParseMode Markdown
case "*html" => ParseMode HTML
case _ => null
val bodyOffset = "*msg".length + _target.length + (if _parseMode eq null then 0 else _parseMode.length) + 1
val entities = ArrayBuffer.empty[MessageEntity]
if (raw.entities ne null) for (e <- raw.entities)
val _parsed = MessageEntity(e.`type`, e.offset - bodyOffset, e.length)
if e.url ne null then _parsed.url(e.url)
if e.user ne null then _parsed.user(e.user)
if e.language ne null then _parsed.language(e.language)
entities += _parsed
MessageToSend(_body, entities toArray, parseMode, target)
case _ => null
}
override def onMessage (using update: Update): Boolean = {
val message = update.message
if message.chat.`type` != Chat.Type.Private then return false
if message.text eq null then return false
if !(message.text startsWith "*msg") then return false
if (!(MornyCoeur.trustedInstance isTrusted message.from.id))
MornyCoeur.extra exec SendSticker(
message.chat.id,
TelegramStickers ID_403
).replyToMessageId(message.messageId)
return true
if (message.text == "*msgsend") {
if (message.replyToMessage eq null) return answer404
val messageToSend = MessageToSend from message.replyToMessage
if ((messageToSend eq null) || (messageToSend.message eq null)) return answer404
val sendResponse = MornyCoeur.getAccount execute messageToSend.toSendMessage()
if (sendResponse isOk) {
MornyCoeur.extra exec SendSticker(
update.message.chat.id,
TelegramStickers ID_SENT
).replyToMessageId(update.message.messageId)
} else {
MornyCoeur.extra exec SendMessage(
update.message.chat.id,
// language=html
s"""<b><u>${sendResponse.errorCode} FAILED</u></b>
|<code>${sendResponse.description}</code>"""
.stripMargin
).replyToMessageId(update.message.messageId).parseMode(ParseMode HTML)
}
return true
}
val messageToSend: MessageToSend =
val raw: Message =
if (message.text == "*msg")
if message.replyToMessage eq null then return answer404
else message.replyToMessage
else if (message.text startsWith "*msg")
message
else return answer404
val _toSend = MessageToSend from raw
if _toSend eq null then return answer404
else _toSend
val targetChatResponse = MornyCoeur.getAccount execute GetChat(messageToSend.targetId)
if (targetChatResponse isOk) {
def getChatDescriptionHTML (chat: Chat): String =
import cc.sukazyo.cono.morny.util.tgapi.formatting.MsgEscape.escapeHtml as h
val _c = TGToString as chat
// language=html
s"""<i><u>${h(chat.id toString)}</u>@${h(chat.`type`.name)}</i>${if (chat.`type` != Chat.Type.Private) ":::" else ""}
|${_c getTypeTag} <b>${h(_c getSafeName)}</b> ${_c getSafeLinkHTML}"""
.stripMargin
MornyCoeur.extra exec SendMessage(
update.message.chat.id,
getChatDescriptionHTML(targetChatResponse.chat)
).parseMode(ParseMode HTML).replyToMessageId(update.message.messageId)
} else {
MornyCoeur.extra exec SendMessage(
update.message.chat.id,
// language=html
s"""<b><u>${targetChatResponse.errorCode} FAILED</u></b>
|<code>${targetChatResponse.description}</code>"""
.stripMargin
).parseMode(ParseMode HTML).replyToMessageId(update.message.messageId)
}
if messageToSend.message eq null then return true
val testSendResponse = MornyCoeur.getAccount execute messageToSend.toSendMessage(update.message.chat.id)
.replyToMessageId(update.message.messageId)
if (!(testSendResponse isOk))
MornyCoeur.extra exec SendMessage(
update.message.chat.id,
// language=html
s"""<b><u>${testSendResponse.errorCode}</u> FAILED</b>
|<code>${testSendResponse.description}</code>"""
.stripMargin
).parseMode(ParseMode HTML).replyToMessageId(update.message.messageId)
true
}
private def answer404 (using update: Update): Boolean =
MornyCoeur.extra exec SendSticker(
update.message.chat.id,
TelegramStickers ID_404
).replyToMessageId(update.message.messageId)
true
}

View File

@ -0,0 +1,80 @@
package cc.sukazyo.cono.morny.bot.event
import cc.sukazyo.cono.morny.bot.api.EventListener
import scala.collection.mutable
import cc.sukazyo.cono.morny.Log.logger
import cc.sukazyo.cono.morny.MornyCoeur
import com.google.gson.GsonBuilder
import com.pengrad.telegrambot.model.Update
import com.pengrad.telegrambot.model.request.ParseMode
import com.pengrad.telegrambot.request.SendMessage
import scala.language.postfixOps
object OnEventHackHandle extends EventListener {
private case class Hacker (from_chat: Long, from_message: Long):
override def toString: String = s"$from_chat/$from_message"
enum HackType:
case USER
case GROUP
case ANY
private val hackers = mutable.HashMap.empty[String, Hacker]
def registerHack (from_message: Long, from_user: Long, from_chat: Long, t: HackType): Unit =
val record = t match
case HackType.USER => s"(($from_user))"
case HackType.GROUP => s"{{$from_chat}}"
case HackType.ANY => "[[]]"
hackers += (record -> Hacker(from_chat, from_message))
logger debug s"add hacker track $record"
private def onEventHacked (chat: Long, fromUser: Long)(using update: Update): Boolean = {
logger debug s"got event signed {{$chat}}(($fromUser))"
val x: Hacker =
if hackers contains s"(($fromUser))" then (hackers remove s"(($fromUser))")get
else if hackers contains s"{{$chat}}" then (hackers remove s"{{$chat}}")get
else if hackers contains "[[]]" then (hackers remove "[[]]")get
else return false
logger debug s"hacked event by $x"
import cc.sukazyo.cono.morny.util.tgapi.formatting.MsgEscape.escapeHtml as h
MornyCoeur.extra exec SendMessage(
x.from_chat,
// language=html
s"<code>${h(GsonBuilder().setPrettyPrinting().create.toJson(update))}</code>"
).parseMode(ParseMode HTML).replyToMessageId(x.from_message toInt)
true
}
override def onMessage (using update: Update): Boolean =
onEventHacked(update.message.chat.id, update.message.from.id)
override def onEditedMessage (using update: Update): Boolean =
onEventHacked(update.editedMessage.chat.id, update.editedMessage.from.id)
override def onChannelPost (using update: Update): Boolean =
onEventHacked(update.channelPost.chat.id, update.channelPost.from.id)
override def onEditedChannelPost (using update: Update): Boolean =
onEventHacked(update.editedChannelPost.chat.id, update.editedChannelPost.from.id)
override def onInlineQuery (using update: Update): Boolean =
onEventHacked(0, update.inlineQuery.from.id)
override def onChosenInlineResult (using update: Update): Boolean =
onEventHacked(0, update.chosenInlineResult.from.id)
override def onCallbackQuery (using update: Update): Boolean =
onEventHacked(0, update.callbackQuery.from.id)
override def onShippingQuery (using update: Update): Boolean =
onEventHacked(0, update.shippingQuery.from.id)
override def onPreCheckoutQuery (using update: Update): Boolean =
onEventHacked(0, update.preCheckoutQuery.from.id)
override def onPoll (using update: Update): Boolean =
onEventHacked(0, 0)
override def onPollAnswer (using update: Update): Boolean =
onEventHacked(0, update.pollAnswer.user.id)
override def onMyChatMemberUpdated (using update: Update): Boolean =
onEventHacked(update.myChatMember.chat.id, update.myChatMember.from.id)
override def onChatMemberUpdated (using update: Update): Boolean =
onEventHacked(update.chatMember.chat.id, update.chatMember.from.id)
override def onChatJoinRequest (using update: Update): Boolean =
onEventHacked(update.chatJoinRequest.chat.id, update.chatJoinRequest.from.id)
}

View File

@ -1,7 +1,8 @@
package cc.sukazyo.cono.morny.bot.event
import cc.sukazyo.cono.morny.MornyCoeur
import cc.sukazyo.cono.morny.bot.api.{EventListener, InlineQueryUnit}
import cc.sukazyo.cono.morny.bot.api.EventListener
import cc.sukazyo.cono.morny.bot.query.InlineQueryUnit
import com.pengrad.telegrambot.model.Update
import com.pengrad.telegrambot.model.request.InlineQueryResult
import com.pengrad.telegrambot.request.AnswerInlineQuery
@ -12,12 +13,12 @@ import scala.reflect.ClassTag
object OnInlineQuery extends EventListener {
override def onInlineQuery (update: Update): Boolean = {
override def onInlineQuery (using update: Update): Boolean = {
val results: List[InlineQueryUnit[_]] = MornyCoeur.queryManager query update
var cacheTime = Int.MaxValue
var isPersonal = InlineQueryUnit.DEFAULT_INLINE_PERSONAL_RESP
var isPersonal = InlineQueryUnit.defaults.IS_PERSONAL
val resultAnswers = ListBuffer[InlineQueryResult[_]]()
for (r <- results) {
if (cacheTime > r.cacheTime) cacheTime = r.cacheTime

View File

@ -0,0 +1,21 @@
package cc.sukazyo.cono.morny.bot.event
import cc.sukazyo.cono.morny.bot.api.EventListener
import cc.sukazyo.cono.morny.MornyCoeur
import cc.sukazyo.cono.morny.daemon.MornyDaemons
import com.pengrad.telegrambot.model.{Message, Update}
object OnMedicationNotifyApply extends EventListener {
override def onEditedMessage (using event: Update): Boolean =
editedMessageProcess(event.editedMessage)
override def onEditedChannelPost (using event: Update): Boolean =
editedMessageProcess(event.editedChannelPost)
private def editedMessageProcess (edited: Message): Boolean = {
if edited.chat.id != MornyCoeur.config.medicationNotifyToChat then return false
MornyDaemons.medicationTimerInstance.refreshNotificationWrite(edited)
true
}
}

View File

@ -0,0 +1,30 @@
package cc.sukazyo.cono.morny.bot.event
import cc.sukazyo.cono.morny.bot.api.EventListener
import cc.sukazyo.cono.morny.MornyCoeur
import com.pengrad.telegrambot.model.Update
import com.pengrad.telegrambot.request.SendMessage
import scala.language.postfixOps
object OnQuestionMarkReply extends EventListener {
private def QUESTION_MARKS = Set('?', '', '¿', '⁈', '⁇', '‽', '❔', '❓')
override def onMessage (using event: Update): Boolean = {
if event.message.text eq null then return false
import cc.sukazyo.cono.morny.util.CommonRandom.probabilityTrue
if !probabilityTrue(8) then return false
for (c <- event.message.text toCharArray)
if !(QUESTION_MARKS contains c) then return false
MornyCoeur.extra exec SendMessage(
event.message.chat.id, event.message.text
).replyToMessageId(event.message.messageId)
true
}
}

View File

@ -0,0 +1,34 @@
package cc.sukazyo.cono.morny.bot.event
import cc.sukazyo.cono.morny.bot.api.EventListener
import cc.sukazyo.cono.morny.util.tgapi.InputCommand
import com.pengrad.telegrambot.model.{Message, Update}
import cc.sukazyo.cono.morny.Log.logger
import cc.sukazyo.cono.morny.MornyCoeur
import cc.sukazyo.cono.morny.bot.command.MornyCommands
object OnTelegramCommand extends EventListener {
override def onMessage (using update: Update): Boolean = {
def _isCommandMessage(message: Message): Boolean =
if message.text eq null then false
else if !(message.text startsWith "/") then false
else if message.text startsWith "/ " then false
else true
if !_isCommandMessage(update.message) then return false
val inputCommand = InputCommand(update.message.text drop 1)
if (!(inputCommand.getCommand matches "^\\w+$"))
logger debug "not command"
false
else if ((inputCommand.getTarget ne null) && (inputCommand.getTarget ne MornyCoeur.getUsername))
logger debug "not morny command"
false
else
logger debug "is command"
MornyCommands.execute(using inputCommand)
}
}

View File

@ -0,0 +1,23 @@
package cc.sukazyo.cono.morny.bot.event
import cc.sukazyo.cono.morny.bot.api.EventListener
import cc.sukazyo.cono.morny.bot.command.MornyCommands
import cc.sukazyo.cono.morny.util.tgapi.InputCommand
import com.pengrad.telegrambot.model.Update
object OnUniMeowTrigger extends EventListener {
override def onMessage (using update: Update): Boolean = {
if update.message.text eq null then return false
var ok = false
for ((name, command) <- MornyCommands.commands_uni)
val _name = "/"+name
if (_name == update.message.text)
command.execute(using InputCommand(_name))
ok = true
ok
}
}

View File

@ -0,0 +1,17 @@
package cc.sukazyo.cono.morny.bot.event
import cc.sukazyo.cono.morny.bot.api.EventListener
import cc.sukazyo.cono.morny.MornyCoeur
import com.pengrad.telegrambot.model.Update
object OnUpdateTimestampOffsetLock extends EventListener {
private def isOutdated (timestamp: Int): Boolean =
timestamp < (MornyCoeur.config.eventOutdatedTimestamp/1000)
override def onMessage (using update: Update): Boolean = isOutdated(update.message.date)
override def onEditedMessage (using update: Update): Boolean = isOutdated(update.editedMessage.date)
override def onChannelPost (using update: Update): Boolean = isOutdated(update.channelPost.date)
override def onEditedChannelPost (using update: Update): Boolean = isOutdated(update.editedChannelPost.date)
}

View File

@ -12,7 +12,7 @@ object OnUserRandom extends EventListener {
private val USER_OR_QUERY = "(.+)(?:还是|or)(.+)"r
private val USER_IF_QUERY = "(.+)[吗?|]+$"r
override def onMessage(update: Update): Boolean = {
override def onMessage(using update: Update): Boolean = {
if update.message.text == null then return false
if update.message.text startsWith "/" then return false

View File

@ -15,9 +15,9 @@ object OnUserSlashAction extends EventListener {
private val TG_FORMAT = "^\\w+(@\\w+)?$"r
override def onMessage (update: Update): Boolean = {
override def onMessage (using update: Update): Boolean = {
val text = update.message.text;
val text = update.message.text
if text == null then return false
if (text startsWith "/") {
@ -31,6 +31,7 @@ object OnUserSlashAction extends EventListener {
case TG_FORMAT(_) =>
return false
case x if x contains "/" => return false
case _ =>
val isHardParse = actions(0) isBlank
def hp_len(i: Int) = if isHardParse then i+1 else i
@ -39,7 +40,7 @@ object OnUserSlashAction extends EventListener {
val hasObject = actions.length != hp_len(1)
val v_object =
if hasObject then
actions slice(hp_len(1), actions.length) mkString(" ")
actions slice(hp_len(1), actions.length) mkString " "
else ""
val origin = update.message
val target =

View File

@ -1,7 +1,6 @@
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 {

View File

@ -0,0 +1,27 @@
package cc.sukazyo.cono.morny.bot.query
import cc.sukazyo.cono.morny.bot.query.InlineQueryUnit.defaults
import com.pengrad.telegrambot.model.request.InlineQueryResult
object InlineQueryUnit {
object defaults:
val CACHE_TIME = 300
val IS_PERSONAL = false
}
class InlineQueryUnit[T <: InlineQueryResult[T]](val result: T) {
var cacheTime: Int = defaults.CACHE_TIME
var isPersonal: Boolean = defaults.IS_PERSONAL
def cacheTime (v: Int): InlineQueryUnit[T] =
this.cacheTime = v
this
def isPersonal (v: Boolean): InlineQueryUnit[T] =
this.isPersonal = v
this
}

View File

@ -1,6 +1,5 @@
package cc.sukazyo.cono.morny.bot.query
import cc.sukazyo.cono.morny.bot.api.InlineQueryUnit
import cc.sukazyo.cono.morny.bot.query
import com.pengrad.telegrambot.model.Update

View File

@ -1,6 +1,5 @@
package cc.sukazyo.cono.morny.bot.query
import cc.sukazyo.cono.morny.bot.api.InlineQueryUnit
import cc.sukazyo.cono.morny.util.tgapi.formatting.NamedUtils.inlineIds
import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramUserInformation
import com.pengrad.telegrambot.model.Update

View File

@ -1,5 +1,4 @@
package cc.sukazyo.cono.morny.bot.query
import cc.sukazyo.cono.morny.bot.api.InlineQueryUnit
import cc.sukazyo.cono.morny.util.tgapi.formatting.NamedUtils.inlineIds
import com.pengrad.telegrambot.model.Update
import com.pengrad.telegrambot.model.request.{InlineQueryResultArticle, InputTextMessageContent}

View File

@ -1,7 +1,6 @@
package cc.sukazyo.cono.morny.bot.query
import cc.sukazyo.cono.morny.Log.logger
import cc.sukazyo.cono.morny.bot.api.InlineQueryUnit
import cc.sukazyo.cono.morny.util.BiliTool
import cc.sukazyo.cono.morny.util.tgapi.formatting.NamedUtils.inlineIds
import com.pengrad.telegrambot.model.Update

View File

@ -1,6 +1,5 @@
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

View File

@ -0,0 +1,17 @@
package cc.sukazyo.cono.morny.data
import com.pengrad.telegrambot.model.User
import scala.language.postfixOps
object MornyJrrp {
def jrrp_of_telegramUser (user: User, timestamp: Long): Double =
jrrp_v_xmomi(user.id, timestamp/(1000*60*60*24)) * 100.0
private def jrrp_v_xmomi (identifier: Long, dayStamp: Long): Double =
import cc.sukazyo.cono.morny.util.CommonConvert.byteArrayToHex
import cc.sukazyo.cono.morny.util.CommonEncrypt.hashMd5
(java.lang.Long parseLong byteArrayToHex(hashMd5(s"$identifier@$dayStamp")).substring(0, 4)) / (0xffff toDouble)
}

View File

@ -0,0 +1,37 @@
package cc.sukazyo.cono.morny.data
import cc.sukazyo.cono.morny.util.OkHttpPublic.MediaTypes
import com.google.gson.Gson
import okhttp3.{OkHttpClient, Request, RequestBody, ResponseBody}
import java.io.IOException
import scala.util.Using
object NbnhhshQuery {
case class Word (name: String, trans: Array[String], inputting: Array[String])
case class GuessResult (words: Array[Word])
private case class GuessRequest (text: String)
private val API_URL = "https://lab.magiconch.com/api/nbnhhsh/"
private val API_GUESS_METHOD = "guess/"
private val httpClient = OkHttpClient()
@throws[IOException]
def sendGuess (text: String): GuessResult = {
val requestJsonText = Gson().toJson(GuessRequest(text))
val request = Request.Builder()
.url(API_URL + API_GUESS_METHOD)
.post(RequestBody.create(requestJsonText, MediaTypes.JSON))
.build
Using (httpClient.newCall(request).execute) { response =>
val body = response.body
if body eq null then throw IOException("Nbnhhsh Request: body is null.")
val x = s"{ 'words': ${body.string} }"
Gson().fromJson(x, classOf[GuessResult])
}.get
}
}

View File

@ -30,12 +30,11 @@ object IP186QueryHandler {
@throws[IOException]
private def commonQuery (requestUrl: String, queryParam: String): IP186Response = {
val request = Request.Builder().url(requestUrl + "?" + queryParam).build
var _body_string: String|Null = null
Using ((httpClient newCall request) execute) { response =>
if response.body ne null then _body_string = response.body.string
}
if _body_string eq null then throw IOException("Response of ip186: body is empty!")
IP186Response(requestUrl, _body_string)
val _body = response.body
if _body eq null then throw IOException("Response of ip186: body is empty!")
IP186Response(requestUrl, _body.string)
}.get
}
}