async def stat_score(bot: NoneBot, ctx: Context_T, args: ParseResult): bm = BattleMaster(ctx['group_id']) now = datetime.now() clan = _check_clan(bm) yyyy, mm, _ = bm.get_yyyymmdd(now) stat = bm.stat_score(1, now) stat.sort(key=lambda x: x[3], reverse=True) if not len(stat): await bot.send(ctx, f"{clan['name']}{yyyy}年{mm}月会战统计数据为空", at_sender=True) return # msg = [ f"\n{yyyy}年{mm}月会战{clan['name']}分数统计:" ] # for _, _, name, score in stat: # score = f'{score:,d}' # 数字太多会被腾讯ban,用逗号分隔 # blank = ' ' * (11-len(score)) # QQ字体非等宽,width(空格*2) == width(数字*1) # msg.append(f"{blank}{score}分 | {name}") # generate statistic figure fig, ax = plt.subplots() score = list(map(lambda i: i[3], stat)) yn = len(stat) name = list(map(lambda i: i[2], stat)) y_pos = list(range(yn)) if score[0] >= 1e8: unit = 1e8 unit_str = 'e' else: unit = 1e4 unit_str = 'w' y_size = 0.3 * yn + 1.0 fig.set_size_inches(10, y_size) bars = ax.barh(y_pos, score, align='center') ax.set_title(f"{clan['name']}{yyyy}年{mm}月会战分数统计") ax.set_yticks(y_pos) ax.set_yticklabels(name) ax.set_ylim((-0.6, yn - 0.4)) ax.invert_yaxis() ax.set_xlabel('分数') ax.ticklabel_format(axis='x', style='plain') for rect in bars: w = rect.get_width() ax.text(w, rect.get_y() + rect.get_height() / 2, f'{w/unit:.2f}{unit_str}', ha='left', va='center') plt.subplots_adjust(left=0.12, right=0.96, top=1 - 0.35 / y_size, bottom=0.55 / y_size) pic = util.fig2b64(plt) plt.close() msg = f"{ms.image(pic)}\n※伤害统计请发送“!伤害统计”" await bot.send(ctx, msg, at_sender=True)
async def send_time_dist(bot, event): gid = event['group_id'] year, month = get_ym() try: name, times = get_time(gid, year, month) except Exception as e: await bot.send(event, f"出现错误: {str(e)}\n请联系开发组调教。") return plt.rcParams['axes.unicode_minus'] = False prop = fm.FontProperties(fname=font_path) prop.set_size('large') fig, ax = plt.subplots(figsize=(12, 6), facecolor='white') ax.set_xlabel('时间', fontproperties=prop) ax.set_ylabel('刀数', fontproperties=prop) ax.set_title(f'{name}{year}年{month}月会战出刀时间统计', fontproperties=prop) ax.set_xlim((0 - 0.5, 24)) ax.spines['top'].set_visible(False) ax.spines['right'].set_visible(False) colors = (['#808080'] * 6) + (['#9bc5af'] * 6) + (['#c54731'] * 6) + (['#3a4a59'] * 6) labels = list(range(5, 24)) + list(range(5)) labels = [str(s) for s in labels] plt.xticks(range(24), labels, fontproperties=prop) plt.bar(range(24), times, color=colors) pic = fig2b64(plt) plt.close() await bot.send(event, ms.image(pic))
async def stat_damage(bot:NoneBot, ctx:Context_T, args:ParseResult): bm = BattleMaster(ctx['group_id']) now = datetime.now() clan = _check_clan(bm) yyyy, mm, _ = bm.get_yyyymmdd(now) stat = bm.stat_damage(1, now) yn = len(stat) if not yn: await bot.send(ctx, f"{clan['name']}{yyyy}年{mm}月会战统计数据为空", at_sender=True) return stat.sort(key=lambda x: x[3][0], reverse=True) name = [ s[2] for s in stat ] y_pos = list(range(yn)) y_size = 0.3 * yn + 1.0 unit = 1e4 unit_str = 'w' # convert to pre-sum for s in stat: d = s[3] d[0] = 0 for i in range(2, 6): d[i] += d[i - 1] pre_sum_dmg = [ [ s[3][b] for s in stat ] for b in range(6) ] # generate statistic figure fig, ax = plt.subplots() fig.set_size_inches(10, y_size) ax.set_title(f"{clan['name']}{yyyy}年{mm}月会战伤害统计") ax.set_yticks(y_pos) ax.set_yticklabels(name) ax.set_ylim((-0.6, yn - 0.4)) ax.invert_yaxis() ax.set_xlabel('伤害') colors = ['#00a2e8', '#22b14c', '#b5e61d', '#fff200', '#ff7f27', '#ed1c24'] bars = [ ax.barh(y_pos, pre_sum_dmg[b], align='center', color=colors[b]) for b in range(5, -1, -1) ] bars.reverse() ax.ticklabel_format(axis='x', style='plain') for b in range(1, 6): for i, rect in enumerate(bars[b]): x = (rect.get_width() + bars[b - 1][i].get_width()) / 2 y = rect.get_y() + rect.get_height() / 2 d = pre_sum_dmg[b][i] - pre_sum_dmg[b - 1][i] if d > unit: ax.text(x, y, f'{d/unit:.0f}{unit_str}', ha='center', va='center') plt.subplots_adjust(left=0.12, right=0.96, top=1 - 0.35 / y_size, bottom=0.55 / y_size) pic = util.fig2b64(plt) plt.close() msg = f"{ms.image(pic)}\n※分数统计请发送“!分数统计”" await bot.send(ctx, msg, at_sender=True)
async def ss_rate(bot, ev): txt = ev.message.extract_plain_text().strip() if not txt: await bot.finish(ev, '请在后面跟魂石数据\n示例:魂石评分 输出/坦克/奶妈天/地/荒攻击4伤害10暴击4气血3', at_sender=True) pfbz = txt[:2] if pfbz not in ss_stds.keys(): await bot.finish( ev, '魂石数据不正确,缺少评分模板(输出/坦克/奶妈)\n示例:魂石评分 输出/坦克/奶妈天/地/荒攻击4伤害10暴击4气血3', at_sender=True) ss_score = ss_stds[pfbz] txt = txt[2:] ss_type = txt[:1] if ss_type not in ss_limit.keys(): await bot.finish( ev, '魂石数据不正确,缺少魂石类型(天地荒)\n示例:魂石评分 输出/坦克/奶妈天/地/荒攻击4伤害10暴击4气血3', at_sender=True) score = 0 errmsg = '' txt = txt[1:] entry = [] label = [] labelmax = [] pct = [] yn = 0 while len(txt) > 0: m = re.match( r'^(攻击|物攻|法攻|伤害|物伤|法伤|穿透|物穿|法穿|暴击|反伤|气血|物免|法免|物防|法防|暴抗)(10|[1-9])(\D|)', txt) if m is None: errmsg = "没有匹配到属性" break else: #判断属性是不是超了限制 attr = m.group(1) if attr == "物攻" or attr == "法攻": attr = "攻击" elif attr == "物伤" or attr == "法伤": attr = "伤害" elif attr == "物穿" or attr == "法穿": attr = "穿透" if ss_limit[ss_type][attr] < int( m.group(2)) or ss_min[ss_type][attr] > int(m.group(2)): errmsg = f"{attr}词条的值{m.group(2)}不在{ss_type}魂石允许值范围内{ss_min[ss_type][attr]}-{ss_limit[ss_type][attr]}" break entry.append(attr) label.append(m.group(2)) labelmax.append(ss_limit[ss_type][attr]) pct.append(round(int(m.group(2)) / ss_limit[ss_type][attr], 2)) score += ss_score[attr] * int(m.group(2)) yn += 1 txt = txt[len(attr) + len(m.group(2)):] if errmsg == '': #生成图片 map_vir = cm.get_cmap(name='autumn_r') norm = plt.Normalize(0, 1) norm_values = norm(pct) colors = map_vir(norm_values) redupct = [round(1 - p, 2) for p in pct] fig, ax = plt.subplots() y_pos = list(range(yn)) fig.set_size_inches(5, 4) bars = ax.barh(y_pos, pct, height=0.5, color=colors, alpha=0.8, align='center') bars2 = ax.barh(y_pos, redupct, height=0.5, left=pct, color="#e3e3e3", alpha=0.8, align='center') plt.text(x=0.5, y=0.82, s=f"{rankLv[ss_std(ss_type,score)]}", fontproperties=my_font, fontsize=40, ha="center", transform=fig.transFigure, color=rankColor[ss_std(ss_type, score)]) plt.text(x=0.78, y=0.82, s=f"{pfbz}:{score}分", fontproperties=my_font, fontsize=16, ha="center", color="#666666", transform=fig.transFigure) ax.tick_params(axis='y', labelsize='x-large') [spine.set_visible(False) for spine in ax.spines.values()] ax.tick_params(bottom=False, left=False, labelbottom=False) ax.set_yticks(y_pos) ax.set_yticklabels(entry, fontproperties=my_font, fontsize="20") ax.set_xticks([]) ax.invert_yaxis() i = 0 for rect in bars: w = rect.get_width() ax.text(w - 0.01, rect.get_y() + rect.get_height() / 2 + 0.03, label[i], color="white", ha='right', va='center', fontproperties=my_font, fontsize='x-large') i += 1 i = 0 for rect2 in bars2: ax.text(1.01, rect2.get_y() + rect2.get_height() / 2 + 0.03, labelmax[i], ha='left', va='center', fontproperties=my_font, fontsize='x-large') i += 1 plt.subplots_adjust(left=0.15, right=0.9, top=0.82, bottom=0.1) ax.text(0, 3.5, "评分方法可输入命令:评分标准 来查看", ha='left', va='center', fontsize='small', color="#444444") pic = util.fig2b64(plt) plt.close() await bot.finish(ev, f'\n{ms.image(pic)}', at_sender=True) else: await bot.finish( ev, f'魂石数据不正确:{errmsg}\n示例:魂石评分 输出/坦克/奶妈天/地/荒攻击4伤害10暴击4气血3\n属性词条缩写:攻击|物攻|法攻|伤害|物伤|法伤|穿透|物穿|法穿|暴击|反伤|气血|物免|法免|物防|法防|暴抗', at_sender=True)