You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python Discord机器人审查功能:搜索特殊字符||时遇错误

问题描述

我用Python开发了一个Discord机器人,用来检测黑名单触发词并审查内容。基础功能正常,但被Discord spoiler标签(||包裹)的词会出问题。我想先定位||的位置,只审查没被包裹的词,但定位时遇到了麻烦。

理想输入输出

  • 输入:"Omg I'm so dead"
  • 输出:"Omg I'm so d||ead||"(当前可正常运行)
  • 输入:"||Omg I'm so dead||"
  • 理想输出:"||Omg I'm so dead||"(当前运行报错)

尝试过程

  1. 第一次使用以下代码:
censor_pos = [m.span() for m in re.finditer('||', mge, flags=0)]

因为|是正则特殊字符,返回了字符串的每个位置。

  1. 改用re.escape()处理后:
censor_pos = [m.span() for m in re.finditer(re.escape('||'), mge, flags=0)]

出现错误:TypeError: expected string or bytes-like object, got 'NoneType'。打印censor_pos能得到正确位置,但后续执行触发错误。

完整代码

审查函数

def Censor_Trigger(mge, word): #function to censor trigger words given a message and a trigger word
    
    censor_pos = [m.span() for m in re.finditer(re.escape('||'), mge, flags=0)] #finds all instances of "||" to know which parts of message are censored
    trigger_pos = [m.span() for m in re.finditer(word, mge, flags=re.IGNORECASE)] #finds all instances of a trigger word, giving positions as tuples in an array
    censored_msg = mge
    print (censor_pos)
    print (trigger_pos)
    y = 0 #variable to account for position change when censoring multiple instances of the same word
    for x in trigger_pos:
        a = 0
        is_censored = False
        for z in censor_pos:
            if  0<=a and (a+1)<(len(censor_pos)):
                if censor_pos[a][1]<=x[0]<=censor_pos[a+1][0]:
                    is_censored = True
                    return
                else:
                    is_censored = False          

            a=a+1
                
        if is_censored == False:
            censored_msg = censored_msg[:x[1]+(y*4)] + "||" + censored_msg[x[1]+(y*4):]
            censored_msg = censored_msg[:(x[0]+1+(y*4))] + "||" + censored_msg[(x[0]+1+(y*4)):]    
            y=y+1
            print(censored_msg)
        else:
            return
        
    return censored_msg

调用代码

trigger_words = ["dead", "trigger", "example", "another"] #list of triggers
for x in trigger_words: #runs the function to censor trigger words for all words in the trigger list
                 Mess = Censor_Trigger(Mess, x)

完整错误信息

[2023-09-03 19:26:01] [ERROR   ] discord.client: Ignoring exception in on_message
Traceback (most recent call last):
  File "C:\Users\user\AppData\Local\Programs\Python\Python311\Lib\site-packages\discord\client.py", line 441, in _run_event
    await coro(*args, **kwargs)
  File "C:\Users\user\Desktop\Discord Bot Fun\Test Code.py", line 17, in on_message
    Mess = Censor_Trigger(Mess, x)
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\user\Desktop\Discord Bot Fun\Test Code.py", line 37, in Censor_Trigger
    censor_pos = [m.span() for m in re.finditer(re.escape('||'), mge, flags=0)] #finds all instances of "||" to know which parts of message are censored
                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\user\AppData\Local\Programs\Python\Python311\Lib\re\__init__.py", line 223, in finditer
    return _compile(pattern, flags).finditer(string)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: expected string or bytes-like object, got 'NoneType'

解决方案

1. 修复NoneType错误根源

错误核心是:函数检测到触发词在||包裹区域内时,直接执行空return(无返回值,即返回None),后续循环调用时Mess被赋值为None,导致下一次调用re.finditer时传入None触发错误。

把函数中所有空return改成返回当前的censored_msg,比如:

if censor_pos[a][1]<=x[0]<=censor_pos[a+1][0]:
    is_censored = True
    return censored_msg  # 返回原消息而非空值

2. 修正包裹区域判断逻辑

原逻辑仅检查触发词是否在第a和a+1个||之间,未处理奇数个||、多组包裹区间的情况。正确做法是先将||位置两两配对成完整包裹区间,再判断触发词是否在任意区间内。

3. 优化后的完整函数

import re

def Censor_Trigger(mge, word):
    # 先处理空输入/None的情况
    if not mge:
        return mge
    
    # 获取所有||的位置并两两配对成包裹区间
    censor_spans = [m.span() for m in re.finditer(re.escape('||'), mge)]
    wrapped_intervals = []
    for i in range(0, len(censor_spans), 2):
        if i + 1 < len(censor_spans):
            wrapped_intervals.append((censor_spans[i][0], censor_spans[i+1][1]))
    
    # 获取所有触发词的位置
    trigger_spans = [m.span() for m in re.finditer(word, mge, flags=re.IGNORECASE)]
    censored_msg = mge
    offset = 0  # 记录插入||导致的位置偏移量,每次插入增加4个字符

    for trigger_start, trigger_end in trigger_spans:
        # 调整触发词位置,适配之前插入操作带来的字符串长度变化
        adjusted_start = trigger_start + offset
        adjusted_end = trigger_end + offset

        # 判断触发词是否被包裹
        is_wrapped = False
        for wrap_start, wrap_end in wrapped_intervals:
            if wrap_start <= adjusted_start and adjusted_end <= wrap_end:
                is_wrapped = True
                break
        
        if not is_wrapped:
            # 在触发词前后插入||,比如dead → d||ead||
            censored_msg = censored_msg[:adjusted_end] + "||" + censored_msg[adjusted_end:]
            censored_msg = censored_msg[:adjusted_start + 1] + "||" + censored_msg[adjusted_start + 1:]
            offset += 4  # 更新偏移量
    
    return censored_msg

测试效果

  • 输入"Omg I'm so dead" → 输出"Omg I'm so d||ead||"
  • 输入"||Omg I'm so dead||" → 输出"||Omg I'm so dead||"
  • 输入"dead ||dead|| dead" → 输出"d||ead|| ||dead|| d||ead||"

内容的提问来源于stack exchange,提问作者Asher

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 22:16:10