mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-05 22:10:21 +00:00
refactor: dedupe line qqbot slack lowercase helpers
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import path from "node:path";
|
||||
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
|
||||
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
|
||||
import WebSocket from "ws";
|
||||
import {
|
||||
clearTokenCache,
|
||||
@@ -242,10 +243,11 @@ export async function startGateway(ctx: GatewayContext): Promise<void> {
|
||||
return;
|
||||
}
|
||||
|
||||
const contentLower = content.toLowerCase();
|
||||
const contentLower = normalizeLowercaseStringOrEmpty(content);
|
||||
const isUrgentCommand = URGENT_COMMANDS.some(
|
||||
(cmd) =>
|
||||
contentLower === cmd.toLowerCase() || contentLower.startsWith(cmd.toLowerCase() + " "),
|
||||
contentLower === normalizeLowercaseStringOrEmpty(cmd) ||
|
||||
contentLower.startsWith(normalizeLowercaseStringOrEmpty(cmd) + " "),
|
||||
);
|
||||
if (isUrgentCommand) {
|
||||
log?.info(
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
* 2. `sendPlainReply` handles plain replies, including markdown images and mixed text/media.
|
||||
*/
|
||||
|
||||
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
|
||||
import {
|
||||
sendC2CMessage,
|
||||
sendDmMessage,
|
||||
@@ -151,7 +152,7 @@ export async function parseAndSendMediaTags(
|
||||
|
||||
const tagCounts = mediaTagMatches.reduce(
|
||||
(acc, m) => {
|
||||
const t = m[1].toLowerCase();
|
||||
const t = normalizeLowercaseStringOrEmpty(m[1]);
|
||||
acc[t] = (acc[t] ?? 0) + 1;
|
||||
return acc;
|
||||
},
|
||||
@@ -184,7 +185,7 @@ export async function parseAndSendMediaTags(
|
||||
sendQueue.push({ type: "text", content: filterInternalMarkers(textBefore) });
|
||||
}
|
||||
|
||||
const tagName = match[1].toLowerCase();
|
||||
const tagName = normalizeLowercaseStringOrEmpty(match[1]);
|
||||
let mediaPath = decodeMediaPath(match[2]?.trim() ?? "", log, prefix);
|
||||
|
||||
if (mediaPath) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import * as path from "path";
|
||||
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
||||
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
|
||||
import {
|
||||
getAccessToken,
|
||||
sendC2CFileMessage,
|
||||
@@ -303,7 +304,7 @@ export async function sendPhoto(
|
||||
return { channel: "qqbot", error: sizeCheck.error! };
|
||||
}
|
||||
const fileBuffer = await readFileAsync(mediaPath);
|
||||
const ext = path.extname(mediaPath).toLowerCase();
|
||||
const ext = normalizeLowercaseStringOrEmpty(path.extname(mediaPath));
|
||||
const mimeTypes: Record<string, string> = {
|
||||
".jpg": "image/jpeg",
|
||||
".jpeg": "image/jpeg",
|
||||
@@ -482,7 +483,7 @@ async function sendVoiceFromLocal(
|
||||
const needsTranscode = shouldTranscodeVoice(mediaPath);
|
||||
|
||||
if (needsTranscode && !transcodeEnabled) {
|
||||
const ext = path.extname(mediaPath).toLowerCase();
|
||||
const ext = normalizeLowercaseStringOrEmpty(path.extname(mediaPath));
|
||||
debugLog(
|
||||
`${prefix} sendVoice: transcode disabled, format ${ext} needs transcode, returning error for fallback`,
|
||||
);
|
||||
@@ -886,7 +887,7 @@ export async function sendText(ctx: OutboundContext): Promise<OutboundResult> {
|
||||
sendQueue.push({ type: "text", content: textBefore });
|
||||
}
|
||||
|
||||
const tagName = match[1].toLowerCase();
|
||||
const tagName = normalizeLowercaseStringOrEmpty(match[1]);
|
||||
|
||||
let mediaPath = match[2]?.trim() ?? "";
|
||||
if (mediaPath.startsWith("MEDIA:")) {
|
||||
@@ -1368,7 +1369,7 @@ async function sendTextAfterMedia(ctx: MediaTargetContext, text: string): Promis
|
||||
/** Extract a lowercase extension from a path or URL, ignoring query and hash segments. */
|
||||
function getCleanExt(filePath: string): string {
|
||||
const cleanPath = filePath.split("?")[0].split("#")[0];
|
||||
return path.extname(cleanPath).toLowerCase();
|
||||
return normalizeLowercaseStringOrEmpty(path.extname(cleanPath));
|
||||
}
|
||||
|
||||
/** Check whether a file is an image using MIME first and extension as fallback. */
|
||||
|
||||
@@ -11,6 +11,7 @@ import fs from "node:fs";
|
||||
import { createRequire } from "node:module";
|
||||
import path from "node:path";
|
||||
import { resolveRuntimeServiceVersion } from "openclaw/plugin-sdk/cli-runtime";
|
||||
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
|
||||
import type { QQBotAccountConfig } from "./types.js";
|
||||
import { debugLog } from "./utils/debug-log.js";
|
||||
import { getHomeDir, getQQBotDataDir, isWindows } from "./utils/platform.js";
|
||||
@@ -134,9 +135,9 @@ const frameworkCommands: Map<string, SlashCommand> = new Map();
|
||||
|
||||
function registerCommand(cmd: SlashCommand): void {
|
||||
if (cmd.requireAuth) {
|
||||
frameworkCommands.set(cmd.name.toLowerCase(), cmd);
|
||||
frameworkCommands.set(normalizeLowercaseStringOrEmpty(cmd.name), cmd);
|
||||
} else {
|
||||
commands.set(cmd.name.toLowerCase(), cmd);
|
||||
commands.set(normalizeLowercaseStringOrEmpty(cmd.name), cmd);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -604,7 +605,9 @@ export async function matchSlashCommand(ctx: SlashCommandContext): Promise<Slash
|
||||
|
||||
// Parse the command name and trailing arguments.
|
||||
const spaceIdx = content.indexOf(" ");
|
||||
const cmdName = (spaceIdx === -1 ? content.slice(1) : content.slice(1, spaceIdx)).toLowerCase();
|
||||
const cmdName = normalizeLowercaseStringOrEmpty(
|
||||
spaceIdx === -1 ? content.slice(1) : content.slice(1, spaceIdx),
|
||||
);
|
||||
const args = spaceIdx === -1 ? "" : content.slice(spaceIdx + 1).trim();
|
||||
|
||||
const cmd = commands.get(cmdName);
|
||||
|
||||
@@ -117,7 +117,7 @@ export function isVoiceAttachment(att: { content_type?: string; filename?: strin
|
||||
if (att.content_type === "voice" || att.content_type?.startsWith("audio/")) {
|
||||
return true;
|
||||
}
|
||||
const ext = att.filename ? path.extname(att.filename).toLowerCase() : "";
|
||||
const ext = att.filename ? normalizeLowercaseStringOrEmpty(path.extname(att.filename)) : "";
|
||||
return [".amr", ".silk", ".slk", ".slac"].includes(ext);
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ export function isAudioFile(filePath: string, mimeType?: string): boolean {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
const ext = path.extname(filePath).toLowerCase();
|
||||
const ext = normalizeLowercaseStringOrEmpty(path.extname(filePath));
|
||||
return [
|
||||
".silk",
|
||||
".slk",
|
||||
@@ -175,10 +175,10 @@ const QQ_NATIVE_VOICE_EXTS = new Set([".silk", ".slk", ".amr", ".wav", ".mp3"]);
|
||||
*/
|
||||
export function shouldTranscodeVoice(filePath: string, mimeType?: string): boolean {
|
||||
// Prefer MIME when it is available.
|
||||
if (mimeType && QQ_NATIVE_VOICE_MIMES.has(mimeType.toLowerCase())) {
|
||||
if (mimeType && QQ_NATIVE_VOICE_MIMES.has(normalizeLowercaseStringOrEmpty(mimeType))) {
|
||||
return false;
|
||||
}
|
||||
const ext = path.extname(filePath).toLowerCase();
|
||||
const ext = normalizeLowercaseStringOrEmpty(path.extname(filePath));
|
||||
if (QQ_NATIVE_VOICE_EXTS.has(ext)) {
|
||||
return false;
|
||||
}
|
||||
@@ -510,7 +510,7 @@ export async function audioFileToSilkBase64(
|
||||
return null;
|
||||
}
|
||||
|
||||
const ext = path.extname(filePath).toLowerCase();
|
||||
const ext = normalizeLowercaseStringOrEmpty(path.extname(filePath));
|
||||
|
||||
const uploadFormats = directUploadFormats
|
||||
? normalizeFormats(directUploadFormats)
|
||||
|
||||
@@ -4,6 +4,7 @@ import * as path from "node:path";
|
||||
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
||||
import { fetchRemoteMedia } from "openclaw/plugin-sdk/media-runtime";
|
||||
import type { SsrFPolicy } from "openclaw/plugin-sdk/ssrf-runtime";
|
||||
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
|
||||
|
||||
/** Maximum file size accepted by the QQ Bot API. */
|
||||
export const MAX_UPLOAD_SIZE = 20 * 1024 * 1024;
|
||||
@@ -92,7 +93,7 @@ export function formatFileSize(bytes: number): string {
|
||||
|
||||
/** Infer a MIME type from the file extension. */
|
||||
export function getMimeType(filePath: string): string {
|
||||
const ext = path.extname(filePath).toLowerCase();
|
||||
const ext = normalizeLowercaseStringOrEmpty(path.extname(filePath));
|
||||
const mimeTypes: Record<string, string> = {
|
||||
".jpg": "image/jpeg",
|
||||
".jpeg": "image/jpeg",
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
|
||||
import { expandTilde } from "./platform.js";
|
||||
|
||||
// Canonical media tags. `qqmedia` is the generic auto-routing tag.
|
||||
@@ -84,7 +85,7 @@ const FUZZY_MEDIA_TAG_REGEX = new RegExp(
|
||||
|
||||
/** Normalize a raw tag name into the canonical tag set. */
|
||||
function resolveTagName(raw: string): (typeof VALID_TAGS)[number] {
|
||||
const lower = raw.toLowerCase();
|
||||
const lower = normalizeLowercaseStringOrEmpty(raw);
|
||||
if ((VALID_TAGS as readonly string[]).includes(lower)) {
|
||||
return lower as (typeof VALID_TAGS)[number];
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
|
||||
import type { RefAttachmentSummary } from "../ref-index-store.js";
|
||||
|
||||
/** Replace QQ face tags with readable text labels. */
|
||||
@@ -62,7 +63,7 @@ export function buildAttachmentSummaries(
|
||||
return undefined;
|
||||
}
|
||||
return attachments.map((att, idx) => {
|
||||
const ct = att.content_type?.toLowerCase() ?? "";
|
||||
const ct = normalizeLowercaseStringOrEmpty(att.content_type);
|
||||
let type: RefAttachmentSummary["type"] = "unknown";
|
||||
if (ct.startsWith("image/")) {
|
||||
type = "image";
|
||||
|
||||
Reference in New Issue
Block a user