diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8cd46b5..e7bcb5d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,16 @@ 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.5] - 2025-02-21
+
+### Added
+
+- **Exact match option** – When enabled, the entire message must exactly match the trigger (e.g. `!` triggers only on messages that are just `!`, not `hello!`).
+
+### Fixed
+
+- **Cross-world friends for auto-accept** – Auto-accept now works when a friend invites you from another world or while visiting another world (fallback to name-only match when world ID differs).
+
## [1.0.4] - 2025-02-20
### Added
diff --git a/HSRTools/Configuration/FriendFcCache.cs b/HSRTools/Configuration/FriendFcCache.cs
index 93ad8da..50e82dd 100644
--- a/HSRTools/Configuration/FriendFcCache.cs
+++ b/HSRTools/Configuration/FriendFcCache.cs
@@ -24,6 +24,13 @@ public class FriendFcCache
return Friends.Exists(p => p.Name.Equals(name, StringComparison.OrdinalIgnoreCase) && p.WorldId == worldId);
}
+ /// Name-only match for cross-world friends (e.g. friend visiting another world).
+ public bool IsInFriendsByName(string name)
+ {
+ if (Friends == null || Friends.Count == 0) return false;
+ return Friends.Exists(p => p.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
+ }
+
public bool IsInFreeCompany(string name, ushort worldId)
{
if (FreeCompanyMembers == null || FreeCompanyMembers.Count == 0) return false;
diff --git a/HSRTools/Configuration/HSRToolsConfiguration.cs b/HSRTools/Configuration/HSRToolsConfiguration.cs
index 9e11402..3169410 100644
--- a/HSRTools/Configuration/HSRToolsConfiguration.cs
+++ b/HSRTools/Configuration/HSRToolsConfiguration.cs
@@ -15,6 +15,12 @@ public class HSRToolsConfiguration
///
public bool CaseSensitive { get; set; } = false;
+ ///
+ /// When true, the entire message must exactly match the trigger (after trimming).
+ /// When false, the message only needs to contain the trigger anywhere.
+ ///
+ public bool TriggerExactMatch { get; set; } = false;
+
///
/// Whether to monitor Free Company chat.
///
diff --git a/HSRTools/HSRTools.csproj b/HSRTools/HSRTools.csproj
index 3f15c14..8c3e1e3 100644
--- a/HSRTools/HSRTools.csproj
+++ b/HSRTools/HSRTools.csproj
@@ -1,7 +1,7 @@
true
- 1.0.4.0
+ 1.0.5.0
Knack117
HSRTools
HSRTools
diff --git a/HSRTools/Services/AutoAcceptPartyService.cs b/HSRTools/Services/AutoAcceptPartyService.cs
index 422c3a2..d9eb3a5 100644
--- a/HSRTools/Services/AutoAcceptPartyService.cs
+++ b/HSRTools/Services/AutoAcceptPartyService.cs
@@ -126,7 +126,7 @@ public sealed class AutoAcceptPartyService
if (_config.Debug)
_log.Info($"[AutoAccept] Inviter: '{inviterName}' (worldId={inviterWorldId})");
- var isFriend = _config.AutoAcceptFromFriends && (IsFriendCached(inviterName, inviterWorldId) || IsFriend(inviterName, inviterWorldId));
+ var isFriend = _config.AutoAcceptFromFriends && (IsFriendCached(inviterName, inviterWorldId) || IsFriend(inviterName, inviterWorldId) || IsFriendCachedByName(inviterName));
var isFcMember = _config.AutoAcceptFromFreeCompany && (IsFreeCompanyMemberCached(inviterName, inviterWorldId) || IsFreeCompanyMember(inviterName, inviterWorldId));
if (_config.Debug)
@@ -159,6 +159,7 @@ public sealed class AutoAcceptPartyService
}
private bool IsFriendCached(string characterName, ushort worldId) => _cacheService.IsInFriends(characterName, worldId);
+ private bool IsFriendCachedByName(string characterName) => _cacheService.IsInFriendsByName(characterName);
private bool IsFreeCompanyMemberCached(string characterName, ushort worldId) => _cacheService.IsInFreeCompany(characterName, worldId);
private unsafe bool IsFriend(string characterName, ushort worldId)
diff --git a/HSRTools/Services/ChatMonitorService.cs b/HSRTools/Services/ChatMonitorService.cs
index efb40bc..b7b586e 100644
--- a/HSRTools/Services/ChatMonitorService.cs
+++ b/HSRTools/Services/ChatMonitorService.cs
@@ -150,8 +150,16 @@ public sealed class ChatMonitorService
var trigger = _config.TriggerText;
var comparison = _config.CaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase;
- if (!messageText.Contains(trigger, comparison))
- return;
+ if (_config.TriggerExactMatch)
+ {
+ if (!messageText.Trim().Equals(trigger, comparison))
+ return;
+ }
+ else
+ {
+ if (!messageText.Contains(trigger, comparison))
+ return;
+ }
// Read log module first for non-tells (sender data only valid at callback start; overwritten quickly).
var (logContentId, logWorldId) = type != XivChatType.TellIncoming
diff --git a/HSRTools/Services/FriendFcCacheService.cs b/HSRTools/Services/FriendFcCacheService.cs
index 120fd10..811fac9 100644
--- a/HSRTools/Services/FriendFcCacheService.cs
+++ b/HSRTools/Services/FriendFcCacheService.cs
@@ -86,6 +86,12 @@ public sealed class FriendFcCacheService
lock (_cacheLock) { return _cache.IsInFriends(name, worldId); }
}
+ /// Name-only match for cross-world friends (friend visiting another world).
+ public bool IsInFriendsByName(string name)
+ {
+ lock (_cacheLock) { return _cache.IsInFriendsByName(name); }
+ }
+
public bool IsInFreeCompany(string name, ushort worldId)
{
lock (_cacheLock) { return _cache.IsInFreeCompany(name, worldId); }
diff --git a/HSRTools/UI/ConfigWindow.cs b/HSRTools/UI/ConfigWindow.cs
index 208570a..3ec2b2b 100644
--- a/HSRTools/UI/ConfigWindow.cs
+++ b/HSRTools/UI/ConfigWindow.cs
@@ -9,6 +9,7 @@ public sealed class ConfigWindow : Window
private readonly HSRToolsConfiguration _config;
private string _triggerTextInput = string.Empty;
private bool _caseSensitive;
+ private bool _triggerExactMatch;
private bool _monitorFreeCompany;
private bool _monitorLinkShell;
private bool _monitorCrossWorldLinkShell;
@@ -32,6 +33,7 @@ public sealed class ConfigWindow : Window
{
_triggerTextInput = _config.TriggerText ?? string.Empty;
_caseSensitive = _config.CaseSensitive;
+ _triggerExactMatch = _config.TriggerExactMatch;
_monitorFreeCompany = _config.MonitorFreeCompany;
_monitorLinkShell = _config.MonitorLinkShell;
_monitorCrossWorldLinkShell = _config.MonitorCrossWorldLinkShell;
@@ -51,6 +53,7 @@ public sealed class ConfigWindow : Window
{
_config.TriggerText = "inv";
_config.CaseSensitive = false;
+ _config.TriggerExactMatch = false;
_config.MonitorFreeCompany = true;
_config.MonitorLinkShell = true;
_config.MonitorCrossWorldLinkShell = true;
@@ -74,8 +77,10 @@ public sealed class ConfigWindow : Window
if (ImGui.InputText("##trigger", ref _triggerTextInput, 128))
_config.TriggerText = _triggerTextInput.Trim();
- ImGui.Checkbox("Case sensitive", ref _caseSensitive);
- _config.CaseSensitive = _caseSensitive;
+ if (ImGui.Checkbox("Case sensitive", ref _caseSensitive))
+ _config.CaseSensitive = _caseSensitive;
+ if (ImGui.Checkbox("Exact match (whole message must equal trigger)", ref _triggerExactMatch))
+ _config.TriggerExactMatch = _triggerExactMatch;
ImGui.Separator();
ImGui.Text("Monitor these channels:");