길드용 로스트아크 디스코드 봇 (4) 레이드 가능 인원, embed 형식 출력 구현, 개발 방향성

Jeuk Oh·2021년 7월 28일
0

또 즐거운 로요일입니다. 아브렐슈드 가고싶다..

이번 시간의 목표는, 디스코드 인원들에게 레이드 가능 인원을 알려주는 기능입니다. 이 프로젝트를 시작한 가장 큰 이유이기도 합니다. 지난 시간에 DB 리팩터링을 한 덕에, 이번에는 짧고 간단하게 기능을 추가할 수 있었습니다.

구현 전 사용자 입장에서 생각했던 사용법은 다음과 같습니다.

  1. 레이드를 가기전 or 길드장이 레이드 스케줄을 짜기 위해 가능 인원을 확인

  2. 일일이 물어보기 힘드므로, !인원 or !가능 ~레이드명 명령어로 길드 내 해당 레이드가 가능한 캐릭터들을 출력.

  3. 출력 순은

    [길드원1 :
    캐릭터1, 캐릭터2, ...],
    [길드원2 :
    캐릭터1, 캐릭터2, ...]

부캐와 본캐가 같이 갈 수 없고, 캐릭 당 1번 레이드와 계정 당 1번 레이드가 있기 때문에, 최소한 이정도의 데이터는 보여주어야 합니다. 너무 많은 출력은 보기에 지저분하기 때문에, 이름을 제거하고 직업과 레벨만 보여주기로 하였습니다.

여기서까지가 오늘 글에서 구현할 내용입니다.

먼저 1번의 가능 인원 확인은, DB 함수들을 사용하여 쉽게 구현할 수 있습니다.

@client.command(name='인원',aliases=['가능'],
                    brief='!인원 비아노말',
                    help=f'DB에서 해당 레이드가 가능한 인원을 출력합니다\n'
                         f'아르고스\n발탄노말\n발탄하드\n비아노말\n비아하드\n쿠크리허설\n쿠크노말\n'
                    )
    async def raid_possible(ctx, *args):
        if len(args)==0:
            await ctx.send(f'가능한 명령어\n 아르고스 발탄노말 발탄하드 비아노말 비아하드 쿠크리허설 쿠크노말')
        dungeon_name = args[0]

        lv = [resources.raid.get(dungeon_name)['level_from'],resources.raid.get(dungeon_name)['level_to']]
        #for test
        #lv[1] = 1450
        call_data = DB.load(output=['*'], lv_low=lv[0])
        await ctx.send(embed=embed_print_raid_possible(call_data,dungeon_name,lv[1]))

raid 호출 전체 코드입니다.

client.command 데코레이터로, 디스코드 명령어로 만들 수 있습니다. brief 인자는 디스코드에서 !help를 입력하였을 때 해당 명령어에 대해 나오는 간단한 설명 구문이고,
help 인자는 !help 명령어 를 입력하였을 때 나오는 자세한 구문입니다.
지금은 단순히 타이핑 되어 있지만, 추후 csv 파일에서 던전 리스트를 호출하는 것으로 수정해야겠습니다.

args로 레이드 명을 받아오면, csv에서 레이드명에 따른 최소, 최대 레벨을 긁어옵니다.

raid.csv 파일에는

이름, 보스명, 난이도, 최소레벨, 최대레벨, 공략, 보상, 원정대 1회제한여부

다음과 같은 정보들이 들어가 있고, 예를 들어 발탄 노말과 쿠크세이튼 리허설 이라면,

발탄노말,발탄,노말,1415,MAX,발탄공략,3300골+~~,False
쿠크리허설,쿠크세이튼,튜토리얼,1385,MAX,쿠크공략,200개,True

이렇게 저장되어 있습니다.

args로 들어온 레이드 이름으로 부터 csv에서 최소레벨과 최대레벨을 긁어온 뒤, DB.load()를 통해 DB에서 최소레벨보다 높은 캐릭터를 모두 긁어오기만 하면 끝입니다.

너무 간단합니다. 호호

call_data는 lv_low보다 높은 모든 캐릭터 정보 튜플을 포함한 리스트입니다. 이쁜 출력을 위해 embed_print 함수를 구현합시다.

def embed_print_raid_possible(call_data,raid_dungeon_name,lv_high):
    hash_tmp = None
    main_name_tmp = None
    datadict = {}
    for num, item in enumerate(call_data):
        if hash_tmp == item[0]:
            datadict[main_name_tmp].append(item[3:])
        else:
            main_name_tmp = item[2]
            hash_tmp = item[0]
            datadict[main_name_tmp] = [item[3:]]
            

3번의 출력순을 지키기 위해서, 디스코드 hash를 기준으로 데이터를 구분하면서 저장하는 작업입니다. 커서가 진행하면서 해쉬가 hash_tmp랑 다르면 새로운 원정대를 만난 것이니, datadict에 '대표 캐릭터' 키 값으로 '직업, 레벨' 데이터를 저장하고 hash_tmp를 커서의 hash 값으로 바꿉니다. 커서의 hash가 hash_tmp랑 같다면, 같은 원정대를 만난 것이므로
'대표 캐릭터' 키 값에 캐릭터 정보를 append합니다.


embed = discord.Embed(title=f'{raid_dungeon_name} 인원', color = 0xd42525)
i = 1
tmp_func = lambda x: f'{x} *' if x >= lv_high else x
for name, list_value in datadict.items():
	value = '\n'.join(f'{i+n}. {x[0]} {tmp_func(x[1])}' for n,x in enumerate(list_value))
        i += len(list_value)
        embed.add_field(name=name, value=value,inline=False)
embed.set_footer(text = '* 보상 수령 불가')
    return embed

이후 원정대 이름을 name으로 사용하고, 값으로 가능한 캐릭터 목록을 씁니다. inline=False를 주어 아래로 쭉 나열하게 합니다.

tmp_func으로 만약에 보상을 받을 수 있는 레벨보다 높다면, 구문에 *을 추가 하여 보상을 못받는 레벨이라는 것을 추가합니다.

실행 결과입니다.

각각 원정대들이 보유한 1415 위 캐릭터를 잘 보여줍니다. 만세!


현재까지 길드원들의 계정 정보를 연동, 저장해서, 디스코드 닉네임을 매칭시켜주고 레이드 가능한 인원 명단을 출력해주는 기능들이 완성 되었습니다. 이것만 있어도 길드 운영에 충분히 도움이 되지만

운영 중 느낀 몇가지 단점과 추가해야할 요소들이 있었습니다.

  • 사용자 입장에서 어떤 명령어가 있는지 잘 모른다. !help를 치면 가능 명령어가 나오지만, 그 명령어 자체도 모르는 경우와 쓸 이유를 못느끼는 경우가 있다. 계정연동을 할 이유를 못느껴 관리자가 일일이 해주거나, 부탁을 하는 중입니다. 부탁을 드려도, 사용법을 다시 알려줘야 합니다.

  • 너무 많은 명령어 (!계정연동, !계정검색, !계정업데이트, !계정초기화, !인원)에 사람들이 사용법에 혼란을 느낀다.

  • 반복되는 명령어 콜에 지저분해지는 채팅채널

로스트아크 통합디코 명령어들을 보며 이러한 불편함들을 보완할 방법을 찾을 수 있었습니다.

  • 계정연동의 경우, 반드시 최초에 수행해야하므로, 그리고 계정연동 시 역할이 부여되므로, 역할이 없는 사람은 계정연동만 가능한 채널에 격리시키고 계정연동 사용법을 따라 연동을 수행하게 할 수 있습니다.

  • 자주 사용할 수 있는 명령어는 상호작용 버튼을 통해서 쉽게 사용 가능하게 합니다. (!계정업데이트)

  • 명령어 콜을 콜한 사람에게만 보여주게 하거나, DM으로 주거나, 일정한 시간 후 삭제되게 합니다.

이러한 방향으로 이미 구현된 기능을 업데이트 할 계획입니다.

여기서 더 나아가서 레이드에 관련되어 추가할 기능입니다


현재는 레벨만을 기준으로 필터링한 인원 목록을 보여주지만, 이미 해당 던전을 클리어한 계정도 필터링 하는 것이 보기 좋습니다. (현재 아르고스가 같은 경우에는 가능 캐릭터가 50여 개가 나옵니다. 오히려 너무 많아버리니 인원 목록을 보여주는 의미가 없습니다.)

그러나 던전 클리어 데이터를 어떻게 수집할지가 문제입니다. 계정 연동도 사람들이 능동적으로 쓰지 않는데, 클리어 정보를 사람들이 알아서 잘 입력할 리가 없습니다. ㅜㅜ 더 좋은 접근성을 지닌 기능들이 필요합니다.

제가 생각한 해결법은, 길드 내 기존에 존재하는 레이드 용 음성채팅을 모두 지운 뒤, 음성 채팅을 만들고 지우는 명령어를 추가하고, 음성 채팅이 지워질 때, 음성 채팅에 있었던 사람들에게 해당 레이드를 완료하였는지 물어보는 DM을 통해 데이터를 수집하는 방향입니다. (한 문장에 필요한 기능이 벌써 몇 개인지 쉽지 않겠습니다.)

정리하면

  1. !시작 아르고스 (or 버튼으로 동작) 시 아르고스 레이드 음성채널이 만들어짐
  2. 레이드가 끝난 후, 음성채널을 만든 공대장이, !성공 아르고스 or !실패 아르고스 명령어로 음성채널을 닫음.
    1.1 성공시 음성채널에 있는(었던) 사람들에게 본인들의 캐릭터 명단을 보내서 투표를 통해 어떤 캐릭터로 클리어하였는지 수집하게함
    1.2 실패시 그냥 음성채널을 닫기만 함
  3. 수집 된 데이터를 모아 가능 명단을 만들 때 반영
  4. 매주 로요일 던전 클리어 데이터 초기화

그리고 명령어 사용을 최소화하고 버튼 식 상호작용을 통한 기능 구현을 지향하고자 합니다.

하나를 끝낼 때마다 숙제가 2~3개씩 추가됩니다. 다음 글은 뭐가 먼저 올라오게 될지... 버튼 사용 연습을 위한 계정 관련 기능들 리펙터링 위주의 글을 쓸 것 같습니다.

profile
개발을 재밌게 하고싶습니다.

6개의 댓글

comment-user-thumbnail
2021년 7월 28일

아 그리고 명령어 호출을 위한 구문을 어떻게 통일해야할지도 꽤 고민되는 부분이었습니다.

!비아키스 보상,인원,공략 의 명령어가 초기 생각이었는데 코드를 짜다보니

!보상 레이드명,
!인원 레이드명

으로 기능을 먼저 앞에 쓰는 것이 훨씬 편합니다.

1개의 답글