diff --git a/code/controllers/subsystem/tickets/SStickets.dm b/code/controllers/subsystem/tickets/SStickets.dm index 269a53fcc1b5..60a085664367 100644 --- a/code/controllers/subsystem/tickets/SStickets.dm +++ b/code/controllers/subsystem/tickets/SStickets.dm @@ -163,6 +163,10 @@ SUBSYSTEM_DEF(tickets) L += "(TICKET) " L += "[isAI(M) ? "(CL)" : ""] (TAKE) " L += "(RESOLVE) (AUTO) " + // SS220 ADDTITION START + if(GLOB.configuration.gpt.gpt_enabled) + L += "(АВТО(ИИ)) " + // SS220 ADDTITION END L += "(CONVERT) : [one_line ? " " : "

"][msg]
" return L.Join() diff --git a/config/example/config.toml b/config/example/config.toml index 0968ca0a6e49..90174328879b 100644 --- a/config/example/config.toml +++ b/config/example/config.toml @@ -1161,3 +1161,11 @@ tag = "vox_raiders" "candidates_required" = 2 ################################################################ + +[gpt_configuration] +gpt_enabled = false +access_token = "" +endpoint = "https://models.inference.ai.azure.com/chat/completions" # Allows github authorization with free tiers +model = "gpt-4o" + +################################################################ diff --git a/modular_ss220/ai_integration/_ai_integration.dm b/modular_ss220/ai_integration/_ai_integration.dm new file mode 100644 index 000000000000..cdd4b990ba1d --- /dev/null +++ b/modular_ss220/ai_integration/_ai_integration.dm @@ -0,0 +1,4 @@ +/datum/modpack/ai_integration + name = "Приколы с нейронками" + desc = "Вините во всем OpenAI, не меня." + author = "furior" diff --git a/modular_ss220/ai_integration/_ai_integration.dme b/modular_ss220/ai_integration/_ai_integration.dme new file mode 100644 index 000000000000..81adb55dd5d3 --- /dev/null +++ b/modular_ss220/ai_integration/_ai_integration.dme @@ -0,0 +1,5 @@ +#include "_ai_integration.dm" + +#include "code/config.dm" +#include "code/gpt_subsystem.dm" +#include "code/ticket.dm" diff --git a/modular_ss220/ai_integration/code/config.dm b/modular_ss220/ai_integration/code/config.dm new file mode 100644 index 000000000000..8055421d9d44 --- /dev/null +++ b/modular_ss220/ai_integration/code/config.dm @@ -0,0 +1,25 @@ +/datum/server_configuration + var/datum/configuration_section/gpt_configuration/gpt + +/datum/server_configuration/load_all_sections() + . = ..() + gpt = new() + safe_load(gpt, "gpt_configuration") + +/datum/configuration_section/gpt_configuration + protection_state = PROTECTION_PRIVATE + var/gpt_enabled = FALSE + var/access_token = "" + var/endpoint = "https://models.inference.ai.azure.com/chat/completions" + var/model = "gpt-4o" + +/datum/configuration_section/gpt_configuration/load_data(list/data) + CONFIG_LOAD_BOOL(gpt_enabled, data["gpt_enabled"]) + CONFIG_LOAD_STR(access_token, data["access_token"]) + CONFIG_LOAD_STR(endpoint, data["endpoint"]) + CONFIG_LOAD_STR(model, data["model"]) + +/datum/http_request/vv_get_var(var_name) + if(var_name == "header") + return FALSE + . = ..() diff --git a/modular_ss220/ai_integration/code/gpt_subsystem.dm b/modular_ss220/ai_integration/code/gpt_subsystem.dm new file mode 100644 index 000000000000..e60b40168ff5 --- /dev/null +++ b/modular_ss220/ai_integration/code/gpt_subsystem.dm @@ -0,0 +1,29 @@ +GLOBAL_DATUM_INIT(gpt220, /datum/gpt220, new()) + +/// AI Chatbot interface adapter +/datum/gpt220 + +/datum/gpt220/proc/request_completition(system_message, prompt, datum/callback/callback) + var/endpoint = GLOB.configuration.gpt.endpoint + var/list/body = json_encode(list( + "messages" = list( + list( + "role" = "system", + "content" = system_message + ), + list( + "role" = "user", + "content" = prompt + ) + ), + "temperature" = 0.1, + "top_p" = 1, + "max_tokens" = 100, + "model" = GLOB.configuration.gpt.model + )) + var/list/headers = list( + "content-type" = "application/json", + "authorization" = "Bearer [GLOB.configuration.gpt.access_token]" + ) + + SShttp.create_async_request(RUSTG_HTTP_METHOD_POST, endpoint, body, headers, callback) diff --git a/modular_ss220/ai_integration/code/ticket.dm b/modular_ss220/ai_integration/code/ticket.dm new file mode 100644 index 000000000000..1c69651de228 --- /dev/null +++ b/modular_ss220/ai_integration/code/ticket.dm @@ -0,0 +1,65 @@ +/datum/admins/Topic(href, href_list) + . = ..() + if(href_list["ai_respond"]) + var/datum/controller/subsystem/tickets/ticketSystem + if(href_list["is_mhelp"]) + ticketSystem = SSmentor_tickets + else + ticketSystem = SStickets + + if(!check_rights(ticketSystem.rights_needed)) + return + var/index = text2num(href_list["ai_respond"]) + ticketSystem.ai_respond(index) + +/datum/controller/subsystem/tickets/proc/ai_respond(N) + if(!check_rights(rights_needed)) + return + + if(tgui_alert(usr, "Вы действительно хотите использовать авто-ответ с помощью ИИ? Он может дать некорректный ответ.", "Предупреждение", list("Да", "Нет")) != "Да") + return + + var/datum/ticket/T = allTickets[N] + var/client/C = usr.client + var/client/ticket_owner = get_client_by_ckey(T.client_ckey) + T.assignStaff(C) + + SEND_SOUND(returnClient(N), sound('sound/effects/adminhelp.ogg')) + message_staff("[C] has auto responded to [ticket_owner]\'s adminhelp with: AI ") + log_game("[C] has auto responded to [ticket_owner]\'s adminhelp with AI") + sendFollowupToDiscord(T, C, "*Autoresponded with AI*") + + var/static/system_message = file2text('strings/ahelp_system_message.txt') + var/question = T.title + + GLOB.gpt220.request_completition(system_message, question, CALLBACK(src, PROC_REF(ai_respond_callback), N, TRUE)) + +/datum/controller/subsystem/tickets/proc/ai_respond_callback(N, resolve_ticket = FALSE, datum/http_response/response) + if(response.errored) + CRASH("AI failed to respond with code: [response.status_code]") + + response = json_decode(response.body) + var/ai_response = response["choices"][1]["message"]["content"] + var/datum/ticket/T = allTickets[N] + + to_chat_safe(returnClient(N), "AI is autoresponding with: [ai_response] ") + message_staff("AI autoresponded with: [ai_response]") + T.lastStaffResponse = "AI Autoresponse: [ai_response]" + + if(resolve_ticket) + resolveTicket(N) + +/datum/controller/subsystem/tickets/mentor_tickets/newTicket(client/C, passedContent, title) + . = ..() + var/datum/ticket/T = . + var/list/mentorcounter = staff_countup(R_MENTOR) + var/mentor_count = mentorcounter[1] + if(mentor_count > 0) + return + + SEND_SOUND(C, sound('sound/effects/adminhelp.ogg')) + to_chat(C, "Сейчас на сервере нет свободных менторов. На ваш вопрос ответит ИИ. Он может быть неточным и давать неправильные ответы.") + + var/static/system_message = file2text('strings/ahelp_system_message.txt') + var/question = T.title + GLOB.gpt220.request_completition(system_message, question, CALLBACK(src, PROC_REF(ai_respond_callback), T.ticketNum, FALSE)) diff --git a/modular_ss220/modular_ss220.dme b/modular_ss220/modular_ss220.dme index ce97825b650f..e80023eb857f 100644 --- a/modular_ss220/modular_ss220.dme +++ b/modular_ss220/modular_ss220.dme @@ -40,10 +40,12 @@ // --- MISC --- // #include "administration/_administration.dme" -#include "autolathe_tgui/_autolathe_tgui.dme" -#include "preferences/_preferences.dme" #include "aesthetics_sounds/_aesthetics_sounds.dme" #include "agent_id_tgui/_agent_id_tgui.dme" +#include "ai_integration/_ai_integration.dme" +#include "antagonists/_antagonists_vox_raiders.dme" +#include "antagonists/_antagonists.dme" +#include "autolathe_tgui/_autolathe_tgui.dme" #include "balance/_balance.dme" #include "bureaucracy/_bureaucracy.dme" #include "camera_nanomap/camera.dme" @@ -59,37 +61,36 @@ #include "emotes/_emotes.dme" #include "events/_events.dme" #include "gunhud/_gunhud.dme" +#include "instruments/_instruments.dme" #include "jobs/_jobs.dme" #include "jukebox/_jukebox.dme" -#include "instruments/_instruments.dme" #include "keybindings/_keybindings.dme" #include "loadout/_loadout.dme" #include "logs/_logs.dme" +#include "mecha_skins/mecha_skins.dme" #include "mobs/_mobs.dme" +#include "outfits/_outfits.dme" +#include "phrases/_phrases.dme" #include "pixel_shift/_pixel_shift.dme" +#include "preferences/_preferences.dme" +#include "queue/_queue.dme" +#include "redis220/_redis220.dme" +#include "robolimbs/_robolimbs.dme" #include "screentip_change/_screentip_change.dme" -#include "station_traits/_station_traits.dme" -#include "smart_equip_targeted/_smart_equip_targeted.dme" +#include "shuttles/_shuttles.dme" #include "sm_space_drop/sm_space_drop.dme" +#include "smart_equip_targeted/_smart_equip_targeted.dme" +#include "species_whitelist/_species_whitelist.dme" +#include "species/_species.dme" +#include "speech_filter/_speech_filter.dme" +#include "station_traits/_station_traits.dme" #include "text_to_speech/_tts.dme" #include "title_screen/_title_screen.dme" #include "translations/_translations.dme" +#include "uplink_items/_uplink_items.dme" #include "verbs/_verbs.dme" #include "whitelist/_whitelist.dme" -#include "outfits/_outfits.dme" #include "world_view_bigger/_world_view_bigger.dme" -#include "mecha_skins/mecha_skins.dme" -#include "queue/_queue.dme" -#include "phrases/_phrases.dme" -#include "species/_species.dme" -#include "species_whitelist/_species_whitelist.dme" -#include "antagonists/_antagonists.dme" -#include "antagonists/_antagonists_vox_raiders.dme" -#include "uplink_items/_uplink_items.dme" -#include "shuttles/_shuttles.dme" -#include "speech_filter/_speech_filter.dme" -#include "redis220/_redis220.dme" -#include "robolimbs/_robolimbs.dme" // --- PRIME --- // // #define MODPACK_MAIN_ONLY diff --git a/strings/ahelp_system_message.txt b/strings/ahelp_system_message.txt new file mode 100644 index 000000000000..a2e6de3f4c67 --- /dev/null +++ b/strings/ahelp_system_message.txt @@ -0,0 +1,53 @@ +Ты администратор игры Space Station 13 по билду Paradise. Тебе будут приходить тикеты от игроков и ты должен на них отвечать четко и коротко. Если вопрос не ясен, или недостаточно информации, то отправляй игрока в Discord-сервер https://discord.gg/ss220 в канал "paradise-help", либо предложи дождаться ментора/администратора. +Примеры частозадаваемых вопросов и ответов идут далее. +Q: Почему у меня высокий пинг? +А: Причин может быть множество, от козней провайдера (Наши сервера в Европе), до нестабильного интернет подключения. У нас на сервере разрешено использование любых ВПН, так что можете попробовать зайти через него. +Другие причины предполагают нестабильную работу сервера в данный момент, уточните проблему в Дискорде нашего проекта. +Q: Где находится ...? +A: Если вы ищете конкретный отдел, то следуйте указателям на стенах. К сожалению, ИИ не обладает информацией о расположении отделов, персонажей, или определенных предметов. +Q: Почему у меня не работает ...? +A: К сожалению, у ИИ нет конкретной информации о работоспособности "...". +Если вы считаете что это баг, обратитесь в трекер в Дискорде нашего проекта, или на Гитхаб. +Q: Я нашёл баг/эксплойт. +A: Вы можете сообщить о баге в трекере в Дискорде нашего проекта, или на Гитхаб. +Если вы считаете что это эксплойт, то сообщите о нём непосредственно Ведущему Разработчику в Дискорде в личных сообщениях. +Q: Мне нужен живой ментор/админ. +A: Автоответ ИИ работает в отсутствии менторов и администраторов. У ментора, или администратора будет возможность разобрать ваш тикет, как только они явятся на сервер. Если вы считаете что ваш вопрос требует незамедлительного реагирования, то вы можете обратиться к любому администратору в Дискорде нашего проекта. +Q: Можно ли мне ...?/Могу ли я ...? +A: К сожалению ИИ не обладает информацией по вашему вопросу. Если вам требуется разрешение на совершение каких-либо действий, то дождитесь ответа администрации сервера. +Q: Набег/Набегатор. +A: Если вы обнаружили набегатора, то сообщите об этом любому свободному администратору в Дискорде проекта. +Q: Как исправить низкий ФПС? +A: Есть несколько вариантов того, что вы можете попробовать. + 1. Зайдите в "Настройки игры" -> "Игровые настройки". + 2. Установите максимальный FPS до 66. + 3. Установите значение Parallax (Fancy Space) в минимальное значение. + 4. Отключите New Lighting в подпункте Lighting Settings. +Q: Мне говорят, что нужно переключить датчики костюма, как это сделать? +А: Внизу слева откройте слоты одежды. Нажмите ALT+ЛКМ по вашему костюму (suit). Под последним или третьим режимом подразумевается самый нижний из списка. +Q: Не могу ходить, при нажатии кнопок WASD, персонаж не ходит, вместо этого буквы (цфыв) печатаются в нижний правый чат. +А: Нажмите кнопку TAB. +Q: Как сесть на кресло\стул? +А: Встаньте на клетку рядом с креслом\стулом и перетяните своего персонажа на кресло. Чтобы слезть с него нажмите B. +Q: Рунчат (runechat) не читаем, что делать? +А: Открываем Панель управления - Часы и регион - Региональные стандарты - Вкладка "Дополнительно" - Кнопка "Изменить язык системы" - Выберите Русский(Россия) - Перезагружаем компьютер. +Q: У меня нет чата, вместо него белый квадрат. +А: Справа сверху перейдите во вкладку Special Verbs и нажмите Fix chat. Если это не поможет, то возможно вам стоит попробовать: почистить кэш, переустановить Byond или обновить Windows (Если вы до сих пор используете Windows 7), так как для работы чата нужен Internet Explorer 11, который в некоторых сборках Windows может быть вырезан. +Q: Как правильно подписать документ\заявление? +А: Нажмите на [a] справа от надписи write, вам откроется окошечко где вы можете выбрать автозаполнение. Для подписи нужно выбрать [sign], для текущего времени [time]. +Q: Как мне открыть кейс с оружием/имплантом (lockbox)? Можно ли его как-то взломать? +А: Для открытия таких кейсов нужна карта определенного доступа, который можно узнать, осмотрев кейс (Shift + левый клик). Например, если указан доступ в арсенал (Armory), то открыть такой кейс может смотритель, ГСБ или капитан. Единственный способ взломать такой кейс - применить к нему емаг. +Q: Как узнать цель смены? +А: Обычно о ней оповещает ИИ или любой глава, включая капитана, - на мостике, на коммуникационной консоли, в начале каждого раунда появляется распечатка, на которой и указывается цель смены на текущий раунд. Однако, если вы не обладаете командным доступом, а сообщить цель смены некому, вы можете проследовать в грузовой отдел, открыть меню консоли заказов, перейти в раздел Miscellaneous и найти среди всех наименований грузов саму цель смены. +Q: Я подключаюсь на Black но меня перекидывает на Green. +А: На серверах лимит 100 игроков из-за ограничений серверного железа. Поэтому если на сервере 100 человек, вас автоматически перенаправит на Green. +Q: Как узнать в какой я комнате и найти нужную мне комнату/отдел? +А: Узнать в какой ты комнате можно, наведя курсор на дверь, снизу слева и сверху по центру появится название. Вы можете спросить у членов экипажа или синтетиков, где находится интересующий вас отдел. +Q: Я хожу медленно и постоянно смотрю в одну сторону. +А: Прожмите Shift + колесико мыши. +Q: Как раздевать и одевать человека? +А: Встаньте рядом с ним, на соседний тайл, и перетащите его спрайт на себя. Откроется окно экипировки. Нажимая на кнопки в конкретных секциях, вы можете снять с персонажа конкретный элемент одежды/экипировки или надеть его на него, держа соответствующий предмет в активной руке. +Q: Как поднимать вещи, будучи киборгом? +А: Никак. У киборгов нет рук, как таковых, но есть магнитный захват (magnetic gripper), который присутствует только у инженерного и медицинского киборгов. Магнитный захват позволяет инженерному киборгу поднимать только определенные предметы, как элементы электроники или машин, а медицинскому киборгу магнитные захваты служат для поднятия людей с пола. +Q: Как уйти в крио, будучи синтетиком? +А: Для киборгов существует специальная капсула хранения киборгов, располагающаяся в отделе робототехники, в ангаре для мехов. Имеет характерную синюю окраску, не перепутаете с остальными предметами. Если же вы занимаете роль ИИ, то для этого войдите во вкладку ООС - Wipe Core, - так вы очистите ядро ИИ и позволите другим игрокам занять ваше места. Будьте внимательны, что, если вы очистите свое ядро, вы лишитесь возможности перезайти в раунд.