Thread Tools Display Modes
08-13-22, 01:27 PM   #1
myrroddin
A Pyroguard Emberseer
 
myrroddin's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2008
Posts: 1,240
SendChatMessage is printing nothing

I am testing and debugging sending myself a whisper. This addon, PriceAnswer, should reply to this whisper
Code:
price N item
where N is optional, default of 1 for the quantity, and item can be an itemLink, itemID, or itemName. PriceAnswer ought to reply with gold values for that item based on TradeSkillMaster calculations.

The debug self:Print() statements on lines 158 & 159 of Core.lua do correctly print prices for valid items, so I know PA is getting valid and correct prices from TSM. However, the following SendChatMessage lines do not reply with anything, and I'm out of ideas why that's the case.

Further, I'd hoped to "harden" PA in case someone asked for a price for an invalid item.
Code:
price sally
triggers BugSack saying that lines 186-194 of Core.lua are expecting an itemLink, itemName, or itemID. I thought I'd caught such a situation, but apparently not.

Attached is the full PriceAnswer addon, which should work in any release version of the game. PA does require TradeSkillMaster of course.

Any ideas why SendChatMessage is not whispering senderName (me, for testing), or how to truly "harden" the addon against invalid items?

Edit: here is the TradeSkillMaster Lua API, although I seem to be using it correctly. https://api.tradeskillmaster.com/addon/
Attached Files
File Type: zip PriceAnswer.zip (167.5 KB, 48 views)

Last edited by myrroddin : 08-13-22 at 01:28 PM. Reason: linked TSM Lua API
  Reply With Quote
08-13-22, 04:24 PM   #2
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,313
Don't know what's going on with SendChatMessage() if your debug prints are showing correct information.



Originally Posted by myrroddin View Post
Further, I'd hoped to "harden" PA in case someone asked for a price for an invalid item.
Code:
price sally
triggers BugSack saying that lines 186-194 of Core.lua are expecting an itemLink, itemName, or itemID. I thought I'd caught such a situation, but apparently not.
Honestly, I'd run it through pcall() and let GetItemInfoInstant() handle validation. Not the most elegant solution, but we don't have that many options. That being said, itemName only works if you have the item on you. Not that useful for someone asking for an item they have, but you don't.

Another thing I'd add is stop normal chat traffic from being processed, causing your pattern matching to return nils and throw errors further down.
__________________
WoWInterface AddOns
"All I want is a pretty girl, a decent meal, and the right to shoot lightning at fools."
-Anders (Dragon Age: Origins - Awakening)

Last edited by SDPhantom : 08-13-22 at 04:29 PM.
  Reply With Quote
08-13-22, 04:30 PM   #3
Dridzt
A Pyroguard Emberseer
 
Dridzt's Avatar
AddOn Author - Click to view addons
Join Date: Nov 2005
Posts: 1,359
I want to answer something but the first lines stuck my mind in a loop.

Code:
local addon_folder = "PriceAnswer"
local title = "Title"
if GetLocale() ~= "enUS" then
    title = title .. "-" .. GetLocale()
end
local addon_name = GetAddOnMetadata(addon_folder, title) or GetAddOnMetadata(addon_folder, "Title")
What is this thing?

Why are you hardcoding your addon name when you can do
Code:
local addonName, addonTable = ...
Anyway .. my guess would be you're trying to send control codes through SendChatMessage which will obviously not work.
Local print has no issue with those.

Last edited by Dridzt : 08-13-22 at 04:33 PM.
  Reply With Quote
08-13-22, 11:09 PM   #4
myrroddin
A Pyroguard Emberseer
 
myrroddin's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2008
Posts: 1,240
I apologize for trying to sell sally, possibly to gnomes. Anyway, here is the latest zip.

@SDPhantom: using pcall seems to have stopped the error. I'm still testing.

@Dridzt: That nonsense has been removed, and was from a much older idea that was retired. Also, I don't see any control characters being sent as part of the strings. "Market 73g57s11c" for example, does not have control characters.

Here's what is happening now. Sending price vestige of the eternal to myself sends the debug print, but echoes price vestige of the eternal to myself instead of the price values. This is the same with any item, crafted or not.

Sending price sally to myself correctly whispers the syntax use message.... in an infinite loop.
Attached Files
File Type: zip PriceAnswer.zip (167.6 KB, 50 views)
  Reply With Quote
08-14-22, 03:33 AM   #5
Dridzt
A Pyroguard Emberseer
 
Dridzt's Avatar
AddOn Author - Click to view addons
Join Date: Nov 2005
Posts: 1,359
I'm not going to install TSM just to test this, but I removed it from .toc and spoofed the TSM_API so the addon loads without errors.

Then I hardcoded
outgoingMessageOne = "outgoing message one"
outgoingMessageTwo = "outgoing message two"

to get rid of the TSM logic.

SendChatMessage works absolutely fine.

So I'll say again the problem with SendChatMessage is 99.9% that you are getting something back that includes control codes (like colors) and such messages are silently ignored.

So it is not SendChatMessage that's broken it's your message.

As for the infinite loop, that's a kinda trivial logic bug to fix don't you think
  Reply With Quote
08-14-22, 10:08 AM   #6
myrroddin
A Pyroguard Emberseer
 
myrroddin's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2008
Posts: 1,240
Oh man, I didn't think about colours as being control codes. I will look into that, or write my own number of copper to string code.
  Reply With Quote
08-14-22, 12:42 PM   #7
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,313
Originally Posted by SDPhantom View Post
Another thing I'd add is stop normal chat traffic from being processed, causing your pattern matching to return nils and throw errors further down.
Originally Posted by myrroddin View Post
Sending price sally to myself correctly whispers the syntax use message.... in an infinite loop.
You still didn't address this issue. It's causing the addon to keep responding to its own messages. It'll also trigger parsing if anyone tries to talk to you.
Unregistering the event while processing isn't enough because the reply comes back after you have re-registered it as the message takes time to go to the chat server and return.
__________________
WoWInterface AddOns
"All I want is a pretty girl, a decent meal, and the right to shoot lightning at fools."
-Anders (Dragon Age: Origins - Awakening)

Last edited by SDPhantom : 08-14-22 at 12:44 PM.
  Reply With Quote
08-14-22, 03:36 PM   #8
myrroddin
A Pyroguard Emberseer
 
myrroddin's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2008
Posts: 1,240
I rewrote this function, then saw your reply. I was about to test it, and if it still goes into an infinite loop, I don't know how to stop it, given the delay to the server and back.
Lua Code:
  1. function PriceAnswer:CHAT_MSG_WHISPER(event, ...)
  2.     -- stop listening to the event while we process the incoming message
  3.     self:UnregisterEvent("CHAT_MSG_WHISPER")
  4.    
  5.     local incomingMessage, senderName = ...
  6.     local outgoingMessageOne, outgoingMessageTwo = self:GetOutgoingMessage(incomingMessage) -- need to split returned prices into two; each message must be <= 255 characters
  7.     outgoingMessageOne = outgoingMessageOne or ""
  8.     outgoingMessageTwo = outgoingMessageTwo or ""
  9.  
  10.     -- stop possible infinite loops
  11.     if outgoingMessageOne == incomingMessage then
  12.         self:RegisterEvent("CHAT_MSG_WHISPER")
  13.         return
  14.     end
  15.     if outgoingMessageTwo == incomingMessage then
  16.         self:RegisterEvent("CHAT_MSG_WHISPER")
  17.         return
  18.     end
  19.  
  20.     -- debug prints
  21.     self:Print("Outgoing message one", outgoingMessageOne == "" and UNKNOWN or outgoingMessageOne)
  22.     self:Print("Outgoing message two", outgoingMessageTwo == "" and UNKNOWN or outgoingMessageTwo)
  23.  
  24.     if outgoingMessageOne ~= "" then
  25.         SendChatMessage(outgoingMessageOne, "WHISPER", nil, senderName)
  26.     end
  27.     if outgoingMessageTwo ~= "" then
  28.         SendChatMessage(outgoingMessageTwo, "WHISPER", nil, senderName)
  29.     end
  30.  
  31.     if outgoingMessageOne == "" and outgoingMessageTwo == "" then
  32.         SendChatMessage(format(L["Syntax: '%s N item' without quotes, N is an optional quantity, default 1, item is an item link or itemID"], L[db.trigger]), "WHISPER", nil, senderName)
  33.     end
  34.  
  35.     -- we are done processing the incoming message, listen to the  event again
  36.     self:RegisterEvent("CHAT_MSG_WHISPER")
  37. end
And Dridzt was correct about hidden control strings. I had to write my own function, as I couldn't gsub things in TSM I don't know about. Writing my own function was a simpler solution.
Lua Code:
  1. -- TradeSkillMaster has some weird control characters in TSM_API.FormatMoneyString, build our own version
  2. function PriceAnswer:ConvertToHumanReadable(num_copper)
  3.     local gold_string, silver_string, copper_string = "", "", ""
  4.     local gold, silver, copper
  5.     if num_copper > 0 then
  6.         gold = floor(num_copper / 10000)
  7.         if gold >= 1 then
  8.             gold_string = format("%d" .. L["g"], gold)
  9.         end
  10.         silver = (num_copper / 100) % 100
  11.         if silver >= 1 then
  12.             silver_string = format("%d" .. L["s"], silver)
  13.         end
  14.         copper = num_copper % 100
  15.         if copper >= 1 then
  16.             copper_string = format("%d" .. L["c"], copper)
  17.         end
  18.         return gold_string .. silver_string .. copper_string
  19.     end
  20.     return nil
  21. end
  Reply With Quote
08-14-22, 03:52 PM   #9
myrroddin
A Pyroguard Emberseer
 
myrroddin's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2008
Posts: 1,240
Partial success! If I now send myself a valid item, I do get the prices back in the whisper. However, immediately after that, I get the syntax use whisper in an infinite loop.

When I whisper myself an invalid item, the syntax use goes into infinite loop.

Progress LOL.
  Reply With Quote
08-14-22, 10:23 PM   #10
Kanegasi
A Molten Giant
 
Kanegasi's Avatar
AddOn Author - Click to view addons
Join Date: Apr 2007
Posts: 666
To prevent the infinite loop when testing, hook SendChatMessage, save every message you send in a table, then bail out of processing when the event gets those messages. There's a simple trick to figure out if you're the one that used a hooked function with an "unused" argument.

Lua Code:
  1. --the tracking table
  2.  
  3. PriceAnswerSentMessages={}
  4.  
  5. --the hook
  6.  
  7. hooksecurefunc("SendChatMessage",function(message,_,_,_,_,_,_,mine)
  8.     if mine then
  9.         PriceAnswerSentMessages[message]=1
  10.     end
  11. end)
  12.  
  13. --then put this in PriceAnswer:CHAT_MSG_WHISPER()
  14.  
  15. if PriceAnswerSentMessages[incomingMessage] then return end
  16.  
  17. --finally, add some nils and a true to SendChatMessage
  18.  
  19. if outgoingMessageOne ~= "" then
  20.     SendChatMessage(outgoingMessageOne, "WHISPER", nil, senderName, nil, nil, nil, true)
  21. end
  22. if outgoingMessageTwo ~= "" then
  23.     SendChatMessage(outgoingMessageTwo, "WHISPER", nil, senderName, nil, nil, nil, true)
  24. end
  25.  
  26. if outgoingMessageOne == "" and outgoingMessageTwo == "" then
  27.     SendChatMessage(format(L["Syntax: '%s N item' without quotes, N is an optional quantity, default 1, item is an item link or itemID"], L[db.trigger]), "WHISPER", nil, senderName, nil, nil, nil, true)
  28. end


As for the control characters, there's an obscure function in the UI code that I believe strips them:

message = SubstituteChatMessageBeforeSend(message)

There's no documentation for it though.
  Reply With Quote
08-15-22, 05:04 AM   #11
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,313
How about check if the incoming message is actually the command you're listening for?
Also fixes the false triggers I pointed out at least twice.
Code:
local incomingMessage, senderName = ...
if not incomingMessage:find(("^%s%%s"):format(L[db.trigger]:gsub("(%W)", "%%%1"))) then return end
__________________
WoWInterface AddOns
"All I want is a pretty girl, a decent meal, and the right to shoot lightning at fools."
-Anders (Dragon Age: Origins - Awakening)
  Reply With Quote
08-15-22, 12:29 PM   #12
myrroddin
A Pyroguard Emberseer
 
myrroddin's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2008
Posts: 1,240
Originally Posted by SDPhantom View Post
How about check if the incoming message is actually the command you're listening for?
Also fixes the false triggers I pointed out at least twice.
I was working on a solution yesterday, as I did get a couple of unrelated whispers, and I spammed the poor person

Your solution is more elegant than mine.
  Reply With Quote
08-19-22, 01:57 PM   #13
myrroddin
A Pyroguard Emberseer
 
myrroddin's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2008
Posts: 1,240
PriceAnswer is working, much and many thanks to the help in this forumn and on the WowDev Discord. I just got a request to accept the following syntax. The person making the request changed the trigger from "price" to "?", which is fine.
Code:
?[ItemLink] (doesn't work)
? [ItemLink] (works)
The question is about removing the space for itemLinks, not itemIDs or item names, in a non-breaking way. If I can figure out how to support some or all of these without breaking the match pattern, that would be great.

The best idea I had was adding trim() to the end of the pattern.
Code:
local pattern = "^" .. (L[db.trigger]:gsub("(%W)", "%%%1")) .. ("%s*(%d*)%s*(.*)$"):trim()
Full function code as it stands without further changes:
Lua Code:
  1. function PriceAnswer:GetOutgoingMessage(incomingMessage)
  2.     -- pattern for "price N item" incoming chat messages
  3.     local pattern = "^" .. (L[db.trigger]:gsub("(%W)", "%%%1")) .. "%s*(%d*)%s*(.*)$"
  4.  
  5.     local itemCount, tail = strmatch(incomingMessage, pattern)
  6.  
  7.     itemCount = itemCount and itemCount:trim()
  8.     tail = tail and tail:trim()
  9.  
  10.     -- get the itemID
  11.     local itemID, retOK, ret1 -- use pcall() to validate GetItemInfoInstant()
  12.     if not itemID then
  13.         retOK, ret1 = pcall(GetItemInfoInstant, tail)
  14.         if retOK then
  15.             itemID = ret1
  16.         else
  17.             retOK, ret1 = pcall(GetItemInfoInstant, tonumber(tail))
  18.             if retOK then
  19.                 itemID = ret1
  20.             end
  21.         end
  22.     end
  23.  
  24.     -- the above did not get an itemID
  25.     if not itemID then
  26.         retOK, ret1 = pcall(GetItemInfoInstant, itemCount)
  27.         if retOK then
  28.             itemID = ret1
  29.         else
  30.             retOK, ret1 = pcall(GetItemInfoInstant, tonumber(itemCount))
  31.             if retOK then
  32.                 itemID = ret1
  33.             end
  34.         end
  35.     end -- at this point it does not matter if there is no itemID
  36.  
  37.     -- convert to a TSM item string "i:12345"
  38.     local itemString
  39.     if TSM_API and TSM_API.ToItemString then
  40.         itemString = TSM_API.ToItemString(tostring(tail))
  41.         if not itemString then
  42.             itemString = TSM_API.ToItemString(tostring(itemCount))
  43.             if itemString then
  44.                 itemCount = 1
  45.             end
  46.         end
  47.         if not itemString then
  48.             if itemID then -- check if there is an itemID from the pcall()
  49.                 itemString = TSM_API.ToItemString(tostring(itemID))
  50.                 if not itemString then
  51.                     itemString = "i:" .. tostring(itemID)
  52.                 end
  53.             end
  54.         end
  55.     end
  56.  
  57.     itemCount = tonumber(itemCount) or 1
  58.     if not itemCount or itemCount < 1 then
  59.         itemCount = 1
  60.     end
  61.  
  62.     -- unrelated rest of the function code continues
  63. end

Last edited by myrroddin : 08-19-22 at 02:03 PM. Reason: adjusted code paste
  Reply With Quote
08-19-22, 02:12 PM   #14
myrroddin
A Pyroguard Emberseer
 
myrroddin's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2008
Posts: 1,240
I completely see why removing the space with an itemID will not work, especially if N is passed.
Code:
price 424306 -- 42 4306 would fail
price 42[Silk Cloth] -- should work
price 42Silk Cloth -- might work, depending on APIs
price 42 [Silk Cloth] -- does work, tested
price 42 Silk Cloth -- might work, depending on APIs
  Reply With Quote
08-19-22, 05:45 PM   #15
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,313
Code:
local pattern = "^" .. (L[db.trigger]:gsub("(%W)", "%%%1")) .. "%s*(%d*)%s*(.-)%s*$"
Changing the match-all capture to match the least number of characters (".-" instead of ".*") and adding "%s*" at the end should make it unnecessary to call :trim().
__________________
WoWInterface AddOns
"All I want is a pretty girl, a decent meal, and the right to shoot lightning at fools."
-Anders (Dragon Age: Origins - Awakening)
  Reply With Quote
08-20-22, 11:04 PM   #16
myrroddin
A Pyroguard Emberseer
 
myrroddin's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2008
Posts: 1,240
Thank you. I'll give that a whirl as soon as possible.
  Reply With Quote

WoWInterface » Developer Discussions » Lua/XML Help » SendChatMessage is printing nothing

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off