Add auto-accept party invites from friends and FC members
- New AutoAcceptPartyService uses IAddonLifecycle to detect party invite popup - Checks inviter against InfoProxyFriendList and InfoProxyFreeCompanyMember - Config options: AutoAcceptEnabled, AutoAcceptFromFriends, AutoAcceptFromFreeCompany - Bump version to 1.0.4, update CHANGELOG and README Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
|
||||
## [1.0.4] - 2025-02-20
|
||||
|
||||
### Added
|
||||
|
||||
- **Auto-accept party invites** – Option to automatically accept party invites when the inviter is on your friend list or is a member of your Free Company. Configurable toggles for friends and FC members.
|
||||
|
||||
## [1.0.3] - 2025-02-03
|
||||
|
||||
### Added
|
||||
|
||||
@@ -45,4 +45,22 @@ public class HSRToolsConfiguration
|
||||
/// Use this to diagnose cross-world invite issues.
|
||||
/// </summary>
|
||||
public bool Debug { get; set; } = false;
|
||||
|
||||
// --- Auto-accept party invites ---
|
||||
|
||||
/// <summary>
|
||||
/// When true, automatically accept party invites from friends and/or Free Company members
|
||||
/// (based on the options below).
|
||||
/// </summary>
|
||||
public bool AutoAcceptEnabled { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// When true and AutoAcceptEnabled, accept party invites from players on your friend list.
|
||||
/// </summary>
|
||||
public bool AutoAcceptFromFriends { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// When true and AutoAcceptEnabled, accept party invites from members of your Free Company.
|
||||
/// </summary>
|
||||
public bool AutoAcceptFromFreeCompany { get; set; } = true;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Dalamud.NET.Sdk/14.0.1">
|
||||
<PropertyGroup>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Version>1.0.3.0</Version>
|
||||
<Version>1.0.4.0</Version>
|
||||
<Author>Knack117</Author>
|
||||
<Name>HSRTools</Name>
|
||||
<InternalName>HSRTools</InternalName>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"Author": "Knack117",
|
||||
"Name": "HSRTools",
|
||||
"Punchline": "Auto-invite to party when a trigger word is said in FC, LS, CWLS, or tells.",
|
||||
"Description": "Detects a user-defined trigger word in Free Company, Link Shell, Cross-World Link Shell, and tell chat, then invites the sender to your party. Works for same-world and cross-world (CWLS).",
|
||||
"Punchline": "Auto-invite and auto-accept party invites from friends/FC.",
|
||||
"Description": "Detects a trigger word in FC, LS, CWLS, and tells to auto-invite; optionally auto-accepts party invites from friends and FC members.",
|
||||
"RepoUrl": "https://github.com/Knack117/HSRTools",
|
||||
"Tags": [ "chat", "party", "invite", "automation" ],
|
||||
"AcceptsFeedback": true
|
||||
|
||||
@@ -15,6 +15,7 @@ public sealed class HSRToolsPlugin : IDalamudPlugin
|
||||
|
||||
private readonly string _configDir;
|
||||
private readonly ChatMonitorService _chatMonitorService;
|
||||
private readonly AutoAcceptPartyService _autoAcceptPartyService;
|
||||
private readonly ConfigWindow _configWindow;
|
||||
private readonly WindowSystem _windowSystem;
|
||||
private HSRToolsConfiguration _config;
|
||||
@@ -32,6 +33,11 @@ public sealed class HSRToolsPlugin : IDalamudPlugin
|
||||
PluginServices.PluginLog,
|
||||
_config);
|
||||
|
||||
_autoAcceptPartyService = new AutoAcceptPartyService(
|
||||
PluginServices.AddonLifecycle,
|
||||
PluginServices.PluginLog,
|
||||
_config);
|
||||
|
||||
_configWindow = new ConfigWindow(_config);
|
||||
_windowSystem = new WindowSystem("HSRTools");
|
||||
_windowSystem.AddWindow(_configWindow);
|
||||
@@ -44,12 +50,15 @@ public sealed class HSRToolsPlugin : IDalamudPlugin
|
||||
});
|
||||
_chatMonitorService.SetConfiguration(_config);
|
||||
_chatMonitorService.Start();
|
||||
_autoAcceptPartyService.SetConfiguration(_config);
|
||||
_autoAcceptPartyService.Start();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
PluginServices.CommandManager.RemoveHandler("/hsr");
|
||||
_chatMonitorService.Stop();
|
||||
_autoAcceptPartyService.Stop();
|
||||
_windowSystem.RemoveAllWindows();
|
||||
SaveConfig(_configDir, _config);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
using System;
|
||||
using Dalamud.Game.Addon.Lifecycle;
|
||||
using Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
||||
using Dalamud.Plugin.Services;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI.Info;
|
||||
using HSRTools.Configuration;
|
||||
|
||||
namespace HSRTools.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Automatically accepts party invites when the inviter is a friend or Free Company member.
|
||||
/// Uses IAddonLifecycle to detect when the party invite addon opens, then checks the
|
||||
/// inviter against the friend list and FC roster before accepting.
|
||||
/// </summary>
|
||||
public sealed class AutoAcceptPartyService
|
||||
{
|
||||
private const string PartyInviteAddonName = "PartyInvite";
|
||||
|
||||
private readonly IAddonLifecycle _addonLifecycle;
|
||||
private readonly IPluginLog _log;
|
||||
private HSRToolsConfiguration _config;
|
||||
|
||||
public AutoAcceptPartyService(IAddonLifecycle addonLifecycle, IPluginLog log, HSRToolsConfiguration config)
|
||||
{
|
||||
_addonLifecycle = addonLifecycle;
|
||||
_log = log;
|
||||
_config = config;
|
||||
}
|
||||
|
||||
public void SetConfiguration(HSRToolsConfiguration config)
|
||||
{
|
||||
_config = config;
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
_addonLifecycle.RegisterListener(AddonEvent.PreSetup, PartyInviteAddonName, OnPartyInviteAddon);
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
_addonLifecycle.UnregisterListener(AddonEvent.PreSetup, PartyInviteAddonName, OnPartyInviteAddon);
|
||||
}
|
||||
|
||||
private void OnPartyInviteAddon(AddonEvent eventType, AddonArgs args)
|
||||
{
|
||||
if (!_config.AutoAcceptEnabled)
|
||||
return;
|
||||
|
||||
if (!_config.AutoAcceptFromFriends && !_config.AutoAcceptFromFreeCompany)
|
||||
return;
|
||||
|
||||
unsafe
|
||||
{
|
||||
var agent = AgentPartyInvite.Instance();
|
||||
if (agent == null)
|
||||
return;
|
||||
|
||||
var proxy = agent->InfoProxyPartyInvite;
|
||||
if (proxy == null)
|
||||
return;
|
||||
|
||||
var inviterName = proxy->InviterName.ToString();
|
||||
if (string.IsNullOrWhiteSpace(inviterName))
|
||||
return;
|
||||
|
||||
var inviterWorldId = proxy->InviterWorldId;
|
||||
|
||||
var isFriend = _config.AutoAcceptFromFriends && IsFriend(inviterName, inviterWorldId);
|
||||
var isFcMember = _config.AutoAcceptFromFreeCompany && IsFreeCompanyMember(inviterName, inviterWorldId);
|
||||
|
||||
if (!isFriend && !isFcMember)
|
||||
return;
|
||||
|
||||
var reason = isFriend && isFcMember ? "friend and FC member" : isFriend ? "friend" : "FC member";
|
||||
_log.Info($"Auto-accepting party invite from {inviterName} ({reason}).");
|
||||
|
||||
try
|
||||
{
|
||||
// RespondToInvitation is on InfoProxyInvitedList (parent of InfoProxyPartyInvite)
|
||||
var accepted = ((InfoProxyInvitedList*)proxy)->RespondToInvitation(inviterName, true);
|
||||
if (!accepted)
|
||||
_log.Warning($"Failed to auto-accept party invite from {inviterName}.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Error(ex, $"Error auto-accepting party invite from {inviterName}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static unsafe bool IsFriend(string characterName, ushort worldId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var infoModule = InfoModule.Instance();
|
||||
if (infoModule == null)
|
||||
return false;
|
||||
|
||||
var proxy = (InfoProxyCommonList*)infoModule->GetInfoProxyById(InfoProxyId.FriendList);
|
||||
if (proxy == null)
|
||||
return false;
|
||||
|
||||
var entry = proxy->GetEntryByName(characterName, worldId);
|
||||
return entry != null;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static unsafe bool IsFreeCompanyMember(string characterName, ushort worldId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var infoModule = InfoModule.Instance();
|
||||
if (infoModule == null)
|
||||
return false;
|
||||
|
||||
var proxy = (InfoProxyCommonList*)infoModule->GetInfoProxyById(InfoProxyId.FreeCompanyMember);
|
||||
if (proxy == null)
|
||||
return false;
|
||||
|
||||
var entry = proxy->GetEntryByName(characterName, worldId);
|
||||
return entry != null;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,4 +13,5 @@ public sealed class PluginServices
|
||||
[PluginService] public static IDalamudPluginInterface PluginInterface { get; private set; } = null!;
|
||||
[PluginService] public static IToastGui ToastGui { get; private set; } = null!;
|
||||
[PluginService] public static ICommandManager CommandManager { get; private set; } = null!;
|
||||
[PluginService] public static IAddonLifecycle AddonLifecycle { get; private set; } = null!;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,9 @@ public sealed class ConfigWindow : Window
|
||||
private bool _monitorTell;
|
||||
private bool _enabled;
|
||||
private bool _debug;
|
||||
private bool _autoAcceptEnabled;
|
||||
private bool _autoAcceptFromFriends;
|
||||
private bool _autoAcceptFromFreeCompany;
|
||||
|
||||
public ConfigWindow(HSRToolsConfiguration config)
|
||||
: base("HSRTools Configuration", ImGuiWindowFlags.AlwaysAutoResize)
|
||||
@@ -33,6 +36,9 @@ public sealed class ConfigWindow : Window
|
||||
_monitorTell = _config.MonitorTell;
|
||||
_enabled = _config.Enabled;
|
||||
_debug = _config.Debug;
|
||||
_autoAcceptEnabled = _config.AutoAcceptEnabled;
|
||||
_autoAcceptFromFriends = _config.AutoAcceptFromFriends;
|
||||
_autoAcceptFromFreeCompany = _config.AutoAcceptFromFreeCompany;
|
||||
}
|
||||
|
||||
public override void Draw()
|
||||
@@ -47,6 +53,9 @@ public sealed class ConfigWindow : Window
|
||||
_config.MonitorTell = true;
|
||||
_config.Enabled = true;
|
||||
_config.Debug = false;
|
||||
_config.AutoAcceptEnabled = false;
|
||||
_config.AutoAcceptFromFriends = true;
|
||||
_config.AutoAcceptFromFreeCompany = true;
|
||||
SyncFromConfig();
|
||||
}
|
||||
|
||||
@@ -78,5 +87,19 @@ public sealed class ConfigWindow : Window
|
||||
ImGui.Separator();
|
||||
if (ImGui.Checkbox("Debug logging (diagnose cross-world invite)", ref _debug))
|
||||
_config.Debug = _debug;
|
||||
|
||||
ImGui.Separator();
|
||||
ImGui.Text("Auto-accept party invites:");
|
||||
ImGui.Indent();
|
||||
if (ImGui.Checkbox("Auto-accept from friends and FC members", ref _autoAcceptEnabled))
|
||||
_config.AutoAcceptEnabled = _autoAcceptEnabled;
|
||||
if (_autoAcceptEnabled)
|
||||
{
|
||||
if (ImGui.Checkbox(" From friends", ref _autoAcceptFromFriends))
|
||||
_config.AutoAcceptFromFriends = _autoAcceptFromFriends;
|
||||
if (ImGui.Checkbox(" From Free Company members", ref _autoAcceptFromFreeCompany))
|
||||
_config.AutoAcceptFromFreeCompany = _autoAcceptFromFreeCompany;
|
||||
}
|
||||
ImGui.Unindent();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ A [Dalamud](https://github.com/goatcorp/Dalamud) plugin for FFXIV (XIVLauncher)
|
||||
## Features
|
||||
|
||||
- **Configurable trigger text** – Set any word or phrase (e.g. `inv`, `invite`, `party`). When someone types it in a monitored channel, they are invited to your party.
|
||||
- **Auto-accept party invites** – Optionally automatically accept party invites when the inviter is on your friend list or is a member of your Free Company.
|
||||
- **Channel selection** – Choose which channels to monitor:
|
||||
- Free Company
|
||||
- Link Shell (1–8)
|
||||
@@ -21,6 +22,10 @@ A [Dalamud](https://github.com/goatcorp/Dalamud) plugin for FFXIV (XIVLauncher)
|
||||
|
||||
For **tells**, the game provides the sender’s content ID and world, so invites work reliably. For **FC, LS, and CWLS**, the plugin uses the sender’s content ID when available (e.g. from the last message in CWLS), so cross-world invites work for CWLS and same-world for FC/LS.
|
||||
|
||||
### Auto-accept party invites
|
||||
|
||||
When enabled, the plugin detects incoming party invites and automatically accepts them if the inviter is on your friend list or is a member of your Free Company. You can toggle friends and FC members independently. The friend list and FC roster must have been opened in-game at least once for the plugin to recognize members.
|
||||
|
||||
## Building
|
||||
|
||||
1. Open `HSRTools.sln` in Visual Studio or use the command line.
|
||||
@@ -35,7 +40,7 @@ For **tells**, the game provides the sender’s content ID and world, so invites
|
||||
## Configuration
|
||||
|
||||
- Open the plugin config from the Dalamud plugin list (right-click HSRTools → **Settings**).
|
||||
- Set the trigger text, enable/disable the plugin, choose channels, and set case sensitivity.
|
||||
- Set the trigger text, enable/disable the plugin, choose channels, set case sensitivity, and configure auto-accept (friends/FC).
|
||||
- Settings are saved when you close the game or disable the plugin.
|
||||
|
||||
## Requirements
|
||||
|
||||
Reference in New Issue
Block a user