Backend Development 14 min read

Building a Teamfight Tactics Assistant Bot on the Coze Platform with Node.js Plugins

This article details how to create a Teamfight Tactics assistant bot on the Coze low‑code AI platform, covering background motivation, Coze features, workflow design, Node.js plugin development, API implementation, scheduled data clearing, and future improvement plans.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Building a Teamfight Tactics Assistant Bot on the Coze Platform with Node.js Plugins

Background : The author, a former League of Legends player who now enjoys Teamfight Tactics (TFT), wanted a lightweight tool to query optimal line‑ups, items, and augments without manually browsing slow websites. The goal was to build a Coze bot that returns the needed information with simple user input.

About the Coze Platform : Coze is a next‑generation AI application development platform that allows users, even without programming experience, to build bots powered by large language models. It offers a rich plugin ecosystem, knowledge‑base storage, persistent memory, and visual workflow designers.

Bot Features : The bot provides two main functions – (1) recommending the most popular TFT line‑ups and (2) querying detailed information about chess pieces, items, augments, and traits. Users interact with the bot via natural language; the bot calls underlying workflows to fetch and format data.

Workflow Design : Two separate workflows were created. The first calls a custom "hot lineup" plugin that retrieves the latest lineup JSON from the official TFT site, formats it, and returns a list of line‑ups. The second workflow calls a data‑query plugin that looks up chess, item, or augment information, using a cache backed by MongoDB to improve response speed.

Hot Lineup Plugin (Node.js) :

import { Output } from "@/typings/yunding_hot_list/yunding_hot_list";
import { unescape } from 'querystring';
export async function handler(): Promise
{
  async function getChessMap() {
    const { data } = await fetch('https://game.gtimg.cn/images/lol/act/img/tft/js/chess.js').then(res => res.json());
    return data.reduce((prev, cur) => { prev[cur.chessId] = cur; return prev; }, {});
  }
  async function getHexMap() {
    const { data } = await fetch('https://game.gtimg.cn/images/lol/act/img/tft/js/hex.js').then(res => res.json());
    return Object.values(data).reduce((prev, cur) => { prev[cur.hexId] = cur; return prev; }, {});
  }
  const [chessMap, hexMap] = await Promise.all([getChessMap(), getHexMap()]);
  let data = await fetch('https://game.gtimg.cn/images/lol/act/tftzlkauto/json/lineupJson/s11/6/lineup_detail_total.json').then(res => res.text());
  data = JSON.parse(unescape(data.replace(/\n/g, '')));
  data.lineup_list.forEach(item => { item.detail = JSON.parse(item.detail); });
  const formatted = data.lineup_list.map((item, index) => ({
    rank: index + 1,
    name: item.detail.line_name,
    image: 'https://www.fluxyadmin.cn/file/yunding/1001.jpeg',
    chesses: item.detail.hero_location.map(i => chessMap[i.hero_id]?.displayName).filter(Boolean).join(','),
    hexes: item.detail.hexbuff.recomm.split(',').map(i => hexMap[i]?.name).filter(Boolean).join(','),
    races: item.detail.contact.toSorted((x, y) => y.num - x.num).map(i => `${i.num}${i.name}`).join(' '),
    url: `https://lol.qq.com/tft/#/lineupDetail/${item.id}/1/detail`
  }));
  return { list: formatted };
};

Data Query API (Midway + MongoDB) :

import { Controller, Get, Inject, Query } from '@midwayjs/core';
import { DataService } from '../service/chess.service';
import chess from '../data/chess.json';
import items from '../data/items.json';
import augments from '../data/augments.json';

@Controller('/api')
export class APIController {
  @Inject() dataService: DataService;

  @Get('/data')
  async getData(@Query('name') name: string) {
    const chessName = Object.keys(chess).find(o => o.includes(name));
    if (chessName) return this.dataService.getChessByName(chessName);
    const itemName = Object.keys(items).find(o => o.includes(name));
    if (itemName) return this.dataService.getItemByName(itemName);
    const augmentName = Object.keys(augments).find(o => o.includes(name));
    if (augmentName) return this.dataService.getAugmentByName(augmentName);
  }
}

The service layer contains logic to fetch data from the external tactics.tools API, compute average rankings, and store results in MongoDB. A nightly cron job clears the cache to keep information up‑to‑date:

import { Inject } from '@midwayjs/core';
import { Job, IJob } from '@midwayjs/cron';
import { DataService } from '../service/chess.service';

@Job({ cronTime: '0 0 0 * * *', start: true })
export class DataClearJob implements IJob {
  @Inject() dataService: DataService;
  async onTick() { await this.dataService.clear(); }
}

Development Experience : The author notes several pain points, such as the need to configure custom answer cards, limitations on data types for card bindings, and occasional instability when using Coze's knowledge‑base for large static datasets.

Future Plans : The author intends to improve the knowledge‑base ingestion pipeline, refine answer accuracy, and possibly add more automated data‑refresh mechanisms.

Conclusion : Although the author has not yet reached the Master rank in TFT, the bot significantly eases lineup selection and data lookup, and the open‑source implementation can help other players boost their rank.

BackendNode.jsAPICozeBotgame-assistantTeamfight Tactics
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.