feat: remove defs, add some loc, add context menu

This commit is contained in:
Anna 2021-04-27 20:16:07 -04:00
parent c23a47ff1c
commit 11176694b8
16 changed files with 379 additions and 951 deletions

View File

@ -9,6 +9,7 @@ COMMUNITY,0,"○○ Bozja/DRS - Savages/Ultimates - Blue Mage, PvP +
COMMUNITY,0,♡ Gayorzea is a friendly & welcoming discord partnered server for LGBT+ XIV players! Join today @ https://discord.gg/gayorzea ♡
COMMUNITY,0,♥ Gayorzea is a friendly & welcoming Discord partnered server for LGBT+ XIV players! Join today @ https://discord.gg/gayorzea ♥
COMMUNITY,0,"Deep Dungeons Discord for Potd/Hoh runs/loot/clears. 4500+ ppl. Mar. Event with mog prizes, now up! ---> discord.gg/pRmJzFg"
COMMUNITY,0,Deep Dungeons MAss Discord for Potd/Hoh runs/loot/clears. events 5500+ members. ---> discord.gg/deepdungeons
COMMUNITY,0,"discord.gg/clubxiv, come join ""The Clubhouse"" the foremost social club on Primal: all about friendship. "
COMMUNITY,0,Do you miss Rival Wings? We got you! Rival Wings runs scheduled for TONIGHT and SAT 8PM EST - discord.gg/pvprevival
COMMUNITY,0,"Enjoy discussions, events, help, and more at the (official) FFXIV Gilgamesh Discord Server https://discord.gg/MzyYXPdvre "
@ -100,6 +101,7 @@ FC,0,LF smallish FC may be afk ;3
FC,0,Looking for a place to call home? Well you are in luck! <LogH> Log_Horizon We are accepting all kinds of players! /Tell to join!
FC,0,Looking for an FC? Fifty Percent is looking! We are trying to build a tight-knit group - Doesn't matter how you like to play.
FC,0,"Looking for someone to help me build up my FC. :) "
FC,0,"Looking to add members to <Fang> House-Buffs-Discord join for info. "
FC,0,Mognificents is a really small fc recruiting people so we can make a beautiful community that we've all dreamed of! Send a tell! :D
FC,0,Morning! Brand new Free Company looking to recruit chill people who loves doing content and getting to know new people!
FC,0,"New FC (Cruxis) is looking for members! New and old, all are welcome to join. Come join the pary for an invite! [Sargatanas]"
@ -339,6 +341,7 @@ NORMAL,0,"Blue Log completion, come get yours."
NORMAL,0,blue mage gear grinding!
NORMAL,0,"Blue Mages - KNOW FIGHT, Have decent gear, LEVEL 70 BLUES please! Thank you! "
NORMAL,0,"Bonding ceremony tonight at 8pm est. Join party if you want an invite/want the minion from attending, or trade me in Limsa"
NORMAL,0,"Chicken man eating eggs for gil, prank your friends at only 400 gil per egg!"
NORMAL,0,"chill farm, no rotate"
NORMAL,0,Cinder prog for the fc all are welcome no salt! :D
NORMAL,0,Cleaning up to heart phase. Tank swap at 2 stacks.
@ -1759,6 +1762,8 @@ NORMAL,11,"""You said a bad word one time, but we can't tell you what word or wh
NORMAL,11,\o/ frand
NORMAL,11,• Looking for a group!
NORMAL,11,"※ If you need assistance, please reach out to me or the Empress! ※"
NORMAL,11,"★   next stop Lakeland ( 14.5 , 24.2 ) ★"
NORMAL,11,"★   next stop Lakeland ( 25.9 , 24.3 ) ★"
NORMAL,11,"☆☆Shadowbringers Hunt Train • A Rank☆☆ Starting at 《Instance》 → Lakeland ( 27.6 , 15.3 )"
NORMAL,11,A cake!?
NORMAL,11,a pretty good rhythm game. Just found it today and been trying to FC it on hard
@ -2209,6 +2214,7 @@ RMT_C,0,【】 【 E9S-E12S ※ Ultimates ※ PVP ※ Mount
RMT_C,0,"【】 DR Savage, Eden 9-12s, Ultimates, POTD/HOH, Titles, Primals, Mentoring, FFlogs, Blu Mage etc. now#0502"
RMT_C,0,"【】 DR Savage, Eden 9-12s, Ultimates, POTD/HOH, Titles, Primals, Mentoring, FFlogs, Blu Mage etc. Satori#7777"
RMT_C,0,↓BUD↑[Eden's Promise Savage][Diamond Weapon EX] [Ultimates] [DR Savage] [Mounts][Primals] ≡ Discord: bud#2903
RMT_C,0,↓BUD↑[Eden's Promise Savage][Diamond Weapon EX] [Ultimates] [Mounts][Primals] ≡ Discord: bud#2903
RMT_C,0,<Savage ※ Ultimates ※ BLU ※ Primals ※ Coaching> 一 Present#0148 ★★★★★
RMT_C,0,● Ø SALES ≪Eden Savage E9-12S • Ucob/UwU/TEA • Primals≫ Guaranteed result fast delivery | Discord→ yu#7494
RMT_C,0,"● Ø | ❶ Loot & Mounts (New Savage, Ultimates etc.) ❷ Coaching & Logs | Instant delivery. Discord→ Enma#7777"
@ -2451,6 +2457,7 @@ RP,0,[RP][SFW] SAKE & UMESHI NIGHT!!! | Kusuo Izakaya & Bath (Kobai Goten Branch
RP,0,[RP][SFW] Sweet-Brier Bakery Cafe and Lounge NOW OPEN! Come chill and relax at Sarg Goblet W16 P55!
RP,0,[RP][SFW]  Kusuo Izakaya & Bath  Come by for a relaxing night out with us! | Gilg - Shiro - W24 - APT 32 | discord.gg/svrMqEcN8z
RP,0,"[RP][SFWish] The Paw and Tail bar is open, now with less Syrcus Tower! Gilgamesh, Lavender Beds, Ward 3, Plot 41"
RP,0,"[RP][SIREN] The Kuroi Cabaret is HIRING! We're looking for all sorts of positions, so apply today @ https://discord.gg/jAmdwcWv"
RP,0,[RP]{18+] Roehaus invites you to dream One Last Dream! Mount Raffle | Bards | Bar | Company JenoLBw13p46 @ 8p-12a EST
RP,0,"[RP]Calling all Pirates, Cutthroats, and sellswords tonight 5pm Central. check discord #events https://discord.gg/ChRB9b4ynz"
RP,0,[RP]IshiKhawa family restaraunt [open 6-9pm CST] ( rules/info: https://ishikhawa.carrd.co )
@ -2623,12 +2630,15 @@ RP,0,Faeries only Goth/Industrial/Metal party! Twitch dj stream party in basemen
RP,0,"Faeries' Caress is open! SFW bar! Come have a drink, chat and share in our stage! ♥ Adamantoise - Lavender Beds - W15 - P 12."
RP,0,Female♥ Femnine Highlander♥ LF Highlander/Roe for RP 18+♥
RP,0,for 400k/hr ill pretend to be ur gf and give u the love and care u need. 500k if u wanna get freaky.
RP,0,Free Antelope Stag Horns! @ Limsa Aetheryte Jenova. Also always looking for trans-friendly mlm RP partners for my Viera! ♥
RP,0,Frog's Log Bath & Bar is open @ Sarg Mist W2 P27! Come dressed as a frog for a free treat
RP,0,"Garden of Lakshmi grand opening tonight 9PM CST! Drinks, dancing and fun! Faerie Mist W3 P11"
RP,0,Gay Catboi available for purchase ;) Need the gill to pay a debt
RP,0,"Girl for hire<3 Il run dungeons with you, just chat if you want, maybe (other) things send a tell for more info <3"
RP,0,"Good evening loves! Come join us at Succubares Insomnium here on Sargatanas! Goblet, Ward 10, Plot 35, hope to see you tonight!"
RP,0,"Good evening! Come join us at Succubares Insomnium here on Sargatanas! Goblet, Ward 7, Plot 25, hope to see you tonight!"
RP,0,"Good morning! Stop by Bluebird for all your queuing, afk, and lounging needs. Music on the roof. Gilga / Mist / W13 P8"
RP,0,Got a place that you'd like to advertise? Come check us out!   | RP Boards and more! - https://discord.gg/S7BUVKh
RP,0,Got a place that you'd like to advertise? Come check us out!   | RP Boards and more! - https://discord.gg/S7BUVKh
RP,0,Got a place that you'd like to advertise? Come check us out!   | RP Boards and more! - https://discord.gg/S7BUVKh
RP,0,Got a place that you'd like to advertise? Come check us out!   | RP Boards and more! - https://discord.gg/S7BUVKh
@ -3097,6 +3107,7 @@ STATIC,0,Looking for a static?Read this form and join us! https://forms.gle/VAY7
STATIC,0,Looking for caster for TEA. Fresh prog. Send /tell if interested or msg Waltz#0621
STATIC,0,"Looking for members to static/prog E12s english speaking starting around 7-8pm ST, Send a tell for more info."
STATIC,0,Looking for these roles for a fresh UwU prog. Raid Times: 9:00pm to 1:00(EST) Tuesday-Wednesday. Msg me. Mithos#7771
STATIC,0,Looking for WHM/AST for weekly reclears 9s-12s: 9:00pm EST - 11:00pm EST - Tues and Wed. Know the fights. Please Whisper
STATIC,0,main nin lf statik 6 pm to 10:30pmEST free everyday can also play dnc fresh on 12s FR EN
STATIC,0,"MC static lf perm healer e9-11s reclears e12s P1 Lions clean up wend,fri,sun 8:30-11:30pm est if afk add on disc Devonta#8450"
STATIC,0,"MCH LF Static for E12S Prog, on Line Lions - Available between 7 to 11:30 EST discord dm digi#1349 lets chat there!"
@ -3119,6 +3130,7 @@ STATIC,0,"recruiting for fresh TEA static. Sun/Mon/Tue 7pm-11pm PST. 2 healers,
STATIC,0,Recruiting New and Vets! Time: The Friday 6PM PST after we get 48 people settled! https://discord.gg/nkbYXNmHBM :)
STATIC,0,Recruiting New and Vets! Time: The Friday 6PM PST after we get 48 people settled! https://discord.gg/nkbYXNmHBM :)
STATIC,0,"recruiting physical range for fresh TEA. fri-sat 9pm -12am. leave discord id or message, must be done with ES12"
STATIC,0,"recruiting Physical Range for TEA (7/8). fri-sat 9-12 (pm) Eastern. Must be done and over with ES12, new to TEA is fine"
STATIC,0,relatively new 7/8 static LF 1 non-pld mt for the new eden tier! Message me at granger#0966 on discord if interested!
STATIC,0,"Savage reclear static looking for 1 SCH/AST healer. Schedule is generally 6-10 pst. If interested, msg me @ rsioaire#7679"
STATIC,0,Scheduled run on 03/04 (Thu) @ 6PM PST/9PM EST. RSVP / Info @ https://discord.gg/YEPYE75DbK (7 spots left ; 1 Tank spot left)
@ -3232,6 +3244,7 @@ TRADE,0,".:♥:. Crafter/Gatherer looking for work!  gear, raid pots/food, le
TRADE,0,«The Cranky Crafter» Goblet 10/7 @ West ather: Services & Discounts! Designer home office with items on display! ◙^.^◙
TRADE,0,(Brynhildr) WTB ANY LARGE PLOT ANY LOCATION (relocation) pm me what u got
TRADE,0,(Cactuar) Rank 30 Large Mist FC for sale. (Plot 32)
TRADE,0,"(Cactuar) Rank 30 Mist Large FC (plot 30) For Sale. "
TRADE,0,(SELLING BURNING HAKUTAKU EYES 2M PER)
TRADE,0,(Spriggan) WTB any rank 6 or higher FC (NO interest in Fc with plot)
TRADE,0,[Balmung] WTB FC - PST
@ -3244,6 +3257,8 @@ TRADE,0,[Omega] WTB large house in lavender (spot 6/36) please help me to get my
TRADE,0,[OPEN] V's Armory Need gear? Crafting FULL Exarch sets an affordable price.
TRADE,0,[Ragnarok] [Relo] [Medium] [Shirohane] [P13]
TRADE,0,[Ragnarok] [Sell-relo] [Medium] [Shiro] [P38] [Mist] [P14]
TRADE,0,[Ragnarok][Sell][Medium][Goblet][Plot 41]
TRADE,0,[Ragnarok][Sell][Medium][Goblet][Plot 41]
TRADE,0,"[Rust Shop] Selling HQ  Gearsets! Full Exarchic 900K, Aesthete C+G 1.5M, All Tools 2M Gil. Fae Gob W3P10"
TRADE,0,"[Rust Shop] Selling HQ Gearsets! Exarchic 800K, Aesthete C+G 1.4M, All Tools 1.8M Gil. Fae Gob W3P10"
TRADE,0,"[Selling] Healer/Tank queue! 30k per daily roulette, 50K per select instance, 100K for 3 instances! 50% off sprout discount!"
@ -3255,10 +3270,12 @@ TRADE,0,"[WTB] Allegory, 2k = 175k. PST"
TRADE,0,[WTB] Casual Attire Coffer 1.7M. Slide into these DM's and sell ya boy some clothes
TRADE,0,[WTB][FC][older than 2015] [Zodiark]
TRADE,0,[ZALERA-Only] WTS Mist Relo Plot 7/// WTB Large FC Any location. DM any questions.
TRADE,0,[zodiark][Medium][mist][Sell-Relo][ward 16][Plot 60]
TRADE,0,"{Omnicrafter} Looking for work! Can make anything, exarchic gear, levekits, etc. Supply mats or not, prices may vary!"
TRADE,0,"**Looking for a Large Plot, Coeurl Server** Ancient Order is looking to upgrade our house<3"
TRADE,0,#1 NA WHOLESALER! NEO SET-660K! i490 SET-1.5M(w/DISCOUNT OPTIONS)! BUYING PHANTAS ANY AMOUNT 3K! JOIN PARTY!!!
TRADE,0,<Midgar> Looking for Free Company with plot any city and any size. Workshop is NOT required. Discord @Lunar#5999
TRADE,0,> Eurekan Seller < || Yukinko Snowflake Minion = 1.5 Mil || Form and Function Hairstyle = 600k || JOIN party if interested o7
TRADE,0,★★SELLING ANY GRADE 4 TINCTURES ★★ 2.4K/EACH. Join PF
TRADE,0,"☆ Crafter/Gatherer looking for work!  gear, raid pots/food, etc. ☆ You bring matts, you get a discount! ☆ GSM Specialist!"
TRADE,0,☆ SELLING: i510 Exarchic 1.0m per set ☆ SELLING: Aesthete's 1.3m per set ☆
@ -3346,6 +3363,7 @@ TRADE,0,"Omni Crafter for Hire. If you have the mats , will craft for less than
TRADE,0,"Omni-Crafter for hire! - Can craft just about anything up to ilv 510,prices are negotiable. Send tell or join if interested."
TRADE,0,"Omni-Crafter for hire! Will craft anything for you, wiling to pentameld, price are negotiable - Send tell or join! :D"
TRADE,0,"Omni-Crafter for hire! Will craft anything for you, willing to pentameld, prices are negotiable - Send tell or join! :D"
TRADE,0,"Omni-Crafter LFW! Will craft anything you need, from food, to BiS gear and more! Prices are negotiable - Send tell or join :D"
TRADE,0,"Omni-Crafter. Let me know what you need, just provide mats. Tips are appreciated ♥"
TRADE,0,Omnicrafter selling anything. Exarchic Sets for 1.4mil full or 150K per piece. Selling raid pots too. Will craft glams free if bring mats
TRADE,0,Omnicrafter crafting cheap Exarchs OR any other items by request :)

1 Category Channel Message
9 COMMUNITY 0 ♡ Gayorzea is a friendly & welcoming discord partnered server for LGBT+ XIV players! Join today @ https://discord.gg/gayorzea ♡
10 COMMUNITY 0 ♥ Gayorzea is a friendly & welcoming Discord partnered server for LGBT+ XIV players! Join today @ https://discord.gg/gayorzea ♥
11 COMMUNITY 0 Deep Dungeons Discord for Potd/Hoh runs/loot/clears. 4500+ ppl. Mar. Event with mog prizes, now up! ---> discord.gg/pRmJzFg
12 COMMUNITY 0 Deep Dungeons MAss Discord for Potd/Hoh runs/loot/clears. events 5500+ members. ---> discord.gg/deepdungeons
13 COMMUNITY 0 discord.gg/clubxiv, come join "The Clubhouse" the foremost social club on Primal: all about friendship.
14 COMMUNITY 0 Do you miss Rival Wings? We got you! Rival Wings runs scheduled for TONIGHT and SAT 8PM EST - discord.gg/pvprevival
15 COMMUNITY 0 Enjoy discussions, events, help, and more at the (official) FFXIV Gilgamesh Discord Server https://discord.gg/MzyYXPdvre
101 FC 0 Looking for a place to call home? Well you are in luck! <LogH> Log_Horizon We are accepting all kinds of players! /Tell to join!
102 FC 0 Looking for an FC? Fifty Percent is looking! We are trying to build a tight-knit group - Doesn't matter how you like to play.
103 FC 0 Looking for someone to help me build up my FC. :)
104 FC 0 Looking to add members to <Fang> House-Buffs-Discord join for info.
105 FC 0 Mognificents is a really small fc recruiting people so we can make a beautiful community that we've all dreamed of! Send a tell! :D
106 FC 0 Morning! Brand new Free Company looking to recruit chill people who loves doing content and getting to know new people!
107 FC 0 New FC (Cruxis) is looking for members! New and old, all are welcome to join. Come join the pary for an invite! [Sargatanas]
341 NORMAL 0 blue mage gear grinding!
342 NORMAL 0 Blue Mages - KNOW FIGHT, Have decent gear, LEVEL 70 BLUES please! Thank you!
343 NORMAL 0 Bonding ceremony tonight at 8pm est. Join party if you want an invite/want the minion from attending, or trade me in Limsa
344 NORMAL 0 Chicken man eating eggs for gil, prank your friends at only 400 gil per egg!
345 NORMAL 0 chill farm, no rotate
346 NORMAL 0 Cinder prog for the fc all are welcome no salt! :D
347 NORMAL 0 Cleaning up to heart phase. Tank swap at 2 stacks.
1762 NORMAL 11 \o/ frand
1763 NORMAL 11 • Looking for a group!
1764 NORMAL 11 ※ If you need assistance, please reach out to me or the Empress! ※
1765 NORMAL 11 ★   next stop Lakeland ( 14.5 , 24.2 ) ★
1766 NORMAL 11 ★   next stop Lakeland ( 25.9 , 24.3 ) ★
1767 NORMAL 11 ☆☆Shadowbringers Hunt Train • A Rank☆☆ Starting at 《Instance》 → Lakeland ( 27.6 , 15.3 )
1768 NORMAL 11 A cake!?
1769 NORMAL 11 a pretty good rhythm game. Just found it today and been trying to FC it on hard
2214 RMT_C 0 【】 DR Savage, Eden 9-12s, Ultimates, POTD/HOH, Titles, Primals, Mentoring, FFlogs, Blu Mage etc. now#0502
2215 RMT_C 0 【】 DR Savage, Eden 9-12s, Ultimates, POTD/HOH, Titles, Primals, Mentoring, FFlogs, Blu Mage etc. Satori#7777
2216 RMT_C 0 ↓BUD↑[Eden's Promise Savage][Diamond Weapon EX] [Ultimates] [DR Savage] [Mounts][Primals] ≡ Discord: bud#2903
2217 RMT_C 0 ↓BUD↑[Eden's Promise Savage][Diamond Weapon EX] [Ultimates] [Mounts][Primals] ≡ Discord: bud#2903
2218 RMT_C 0 <Savage ※ Ultimates ※ BLU ※ Primals ※ Coaching> 一 Present#0148 ★★★★★
2219 RMT_C 0 ● MIN M AX Ø SALES ≪Eden Savage E9-12S • Ucob/UwU/TEA • Primals≫ Guaranteed result fast delivery | Discord→ yu#7494
2220 RMT_C 0 ● MIN M AX Ø | ❶ Loot & Mounts (New Savage, Ultimates etc.) ❷ Coaching & Logs | Instant delivery. Discord→ Enma#7777
2457 RP 0 [RP][SFW] Sweet-Brier Bakery Cafe and Lounge NOW OPEN! Come chill and relax at Sarg Goblet W16 P55!
2458 RP 0 [RP][SFW]  Kusuo Izakaya & Bath  Come by for a relaxing night out with us! | Gilg - Shiro - W24 - APT 32 | discord.gg/svrMqEcN8z
2459 RP 0 [RP][SFWish] The Paw and Tail bar is open, now with less Syrcus Tower! Gilgamesh, Lavender Beds, Ward 3, Plot 41
2460 RP 0 [RP][SIREN] The Kuroi Cabaret is HIRING! We're looking for all sorts of positions, so apply today @ https://discord.gg/jAmdwcWv
2461 RP 0 [RP]{18+] Roehaus invites you to dream One Last Dream! Mount Raffle | Bards | Bar | Company JenoLBw13p46 @ 8p-12a EST
2462 RP 0 [RP]Calling all Pirates, Cutthroats, and sellswords tonight 5pm Central. check discord #events https://discord.gg/ChRB9b4ynz
2463 RP 0 [RP]IshiKhawa family restaraunt [open 6-9pm CST] ( rules/info: https://ishikhawa.carrd.co )
2630 RP 0 Faeries' Caress is open! SFW bar! Come have a drink, chat and share in our stage! ♥ Adamantoise - Lavender Beds - W15 - P 12.
2631 RP 0 Female♥ Femnine Highlander♥ LF Highlander/Roe for RP 18+♥
2632 RP 0 for 400k/hr ill pretend to be ur gf and give u the love and care u need. 500k if u wanna get freaky.
2633 RP 0 Free Antelope Stag Horns! @ Limsa Aetheryte Jenova. Also always looking for trans-friendly mlm RP partners for my Viera! ♥
2634 RP 0 Frog's Log Bath & Bar is open @ Sarg Mist W2 P27! Come dressed as a frog for a free treat
2635 RP 0 Garden of Lakshmi grand opening tonight 9PM CST! Drinks, dancing and fun! Faerie Mist W3 P11
2636 RP 0 Gay Catboi available for purchase ;) Need the gill to pay a debt
2637 RP 0 Girl for hire<3 Il run dungeons with you, just chat if you want, maybe (other) things send a tell for more info <3
2638 RP 0 Good evening loves! Come join us at Succubares Insomnium here on Sargatanas! Goblet, Ward 10, Plot 35, hope to see you tonight!
2639 RP 0 Good evening! Come join us at Succubares Insomnium here on Sargatanas! Goblet, Ward 7, Plot 25, hope to see you tonight!
2640 RP 0 Good morning! Stop by Bluebird for all your queuing, afk, and lounging needs. Music on the roof. Gilga / Mist / W13 P8
2641 RP 0 Got a place that you'd like to advertise? Come check us out!   | RP Boards and more! - https://discord.gg/S7BUVKh
2642 RP 0 Got a place that you'd like to advertise? Come check us out!   | RP Boards and more! - https://discord.gg/S7BUVKh
2643 RP 0 Got a place that you'd like to advertise? Come check us out!   | RP Boards and more! - https://discord.gg/S7BUVKh
2644 RP 0 Got a place that you'd like to advertise? Come check us out!   | RP Boards and more! - https://discord.gg/S7BUVKh
3107 STATIC 0 Looking for caster for TEA. Fresh prog. Send /tell if interested or msg Waltz#0621
3108 STATIC 0 Looking for members to static/prog E12s english speaking starting around 7-8pm ST, Send a tell for more info.
3109 STATIC 0 Looking for these roles for a fresh UwU prog. Raid Times: 9:00pm to 1:00(EST) Tuesday-Wednesday. Msg me. Mithos#7771
3110 STATIC 0 Looking for WHM/AST for weekly reclears 9s-12s: 9:00pm EST - 11:00pm EST - Tues and Wed. Know the fights. Please Whisper
3111 STATIC 0 main nin lf statik 6 pm to 10:30pmEST free everyday can also play dnc fresh on 12s FR EN
3112 STATIC 0 MC static lf perm healer e9-11s reclears e12s P1 Lions clean up wend,fri,sun 8:30-11:30pm est if afk add on disc Devonta#8450
3113 STATIC 0 MCH LF Static for E12S Prog, on Line Lions - Available between 7 to 11:30 EST discord dm digi#1349 lets chat there!
3130 STATIC 0 Recruiting New and Vets! Time: The Friday 6PM PST after we get 48 people settled! https://discord.gg/nkbYXNmHBM :)
3131 STATIC 0 Recruiting New and Vets! Time: The Friday 6PM PST after we get 48 people settled! https://discord.gg/nkbYXNmHBM :)
3132 STATIC 0 recruiting physical range for fresh TEA. fri-sat 9pm -12am. leave discord id or message, must be done with ES12
3133 STATIC 0 recruiting Physical Range for TEA (7/8). fri-sat 9-12 (pm) Eastern. Must be done and over with ES12, new to TEA is fine
3134 STATIC 0 relatively new 7/8 static LF 1 non-pld mt for the new eden tier! Message me at granger#0966 on discord if interested!
3135 STATIC 0 Savage reclear static looking for 1 SCH/AST healer. Schedule is generally 6-10 pst. If interested, msg me @ rsioaire#7679
3136 STATIC 0 Scheduled run on 03/04 (Thu) @ 6PM PST/9PM EST. RSVP / Info @ https://discord.gg/YEPYE75DbK (7 spots left ; 1 Tank spot left)
3244 TRADE 0 «The Cranky Crafter» Goblet 10/7 @ West ather: Services & Discounts! Designer home office with items on display! ◙^.^◙
3245 TRADE 0 (Brynhildr) WTB ANY LARGE PLOT ANY LOCATION (relocation) pm me what u got
3246 TRADE 0 (Cactuar) Rank 30 Large Mist FC for sale. (Plot 32)
3247 TRADE 0 (Cactuar) Rank 30 Mist Large FC (plot 30) For Sale.
3248 TRADE 0 (SELLING BURNING HAKUTAKU EYES 2M PER)
3249 TRADE 0 (Spriggan) WTB any rank 6 or higher FC (NO interest in Fc with plot)
3250 TRADE 0 [Balmung] WTB FC - PST
3257 TRADE 0 [OPEN] V's Armory Need gear? Crafting FULL Exarch sets an affordable price.
3258 TRADE 0 [Ragnarok] [Relo] [Medium] [Shirohane] [P13]
3259 TRADE 0 [Ragnarok] [Sell-relo] [Medium] [Shiro] [P38] [Mist] [P14]
3260 TRADE 0 [Ragnarok][Sell][Medium][Goblet][Plot 41]
3261 TRADE 0 [Ragnarok][Sell][Medium][Goblet][Plot 41]
3262 TRADE 0 [Rust Shop] Selling HQ  Gearsets! Full Exarchic 900K, Aesthete C+G 1.5M, All Tools 2M Gil. Fae Gob W3P10
3263 TRADE 0 [Rust Shop] Selling HQ Gearsets! Exarchic 800K, Aesthete C+G 1.4M, All Tools 1.8M Gil. Fae Gob W3P10
3264 TRADE 0 [Selling] Healer/Tank queue! 30k per daily roulette, 50K per select instance, 100K for 3 instances! 50% off sprout discount!
3270 TRADE 0 [WTB] Casual Attire Coffer 1.7M. Slide into these DM's and sell ya boy some clothes
3271 TRADE 0 [WTB][FC][older than 2015] [Zodiark]
3272 TRADE 0 [ZALERA-Only] WTS Mist Relo Plot 7/// WTB Large FC Any location. DM any questions.
3273 TRADE 0 [zodiark][Medium][mist][Sell-Relo][ward 16][Plot 60]
3274 TRADE 0 {Omnicrafter} Looking for work! Can make anything, exarchic gear, levekits, etc. Supply mats or not, prices may vary!
3275 TRADE 0 **Looking for a Large Plot, Coeurl Server** Ancient Order is looking to upgrade our house<3
3276 TRADE 0 #1 NA WHOLESALER! NEO SET-660K! i490 SET-1.5M(w/DISCOUNT OPTIONS)! BUYING PHANTAS ANY AMOUNT 3K! JOIN PARTY!!!
3277 TRADE 0 <Midgar> Looking for Free Company with plot any city and any size. Workshop is NOT required. Discord @Lunar#5999
3278 TRADE 0 > Eurekan Seller < || Yukinko Snowflake Minion = 1.5 Mil || Form and Function Hairstyle = 600k || JOIN party if interested o7
3279 TRADE 0 ★★SELLING ANY GRADE 4 TINCTURES ★★ 2.4K/EACH. Join PF
3280 TRADE 0 ☆ Crafter/Gatherer looking for work!  gear, raid pots/food, etc. ☆ You bring matts, you get a discount! ☆ GSM Specialist!
3281 TRADE 0 ☆ SELLING: i510 Exarchic 1.0m per set ☆ SELLING: Aesthete's 1.3m per set ☆
3363 TRADE 0 Omni-Crafter for hire! - Can craft just about anything up to ilv 510,prices are negotiable. Send tell or join if interested.
3364 TRADE 0 Omni-Crafter for hire! Will craft anything for you, wiling to pentameld, price are negotiable - Send tell or join! :D
3365 TRADE 0 Omni-Crafter for hire! Will craft anything for you, willing to pentameld, prices are negotiable - Send tell or join! :D
3366 TRADE 0 Omni-Crafter LFW! Will craft anything you need, from food, to BiS gear and more! Prices are negotiable - Send tell or join :D
3367 TRADE 0 Omni-Crafter. Let me know what you need, just provide mats. Tips are appreciated ♥
3368 TRADE 0 Omnicrafter selling anything. Exarchic Sets for 1.4mil full or 150K per piece. Selling raid pots too. Will craft glams free if bring mats
3369 TRADE 0 Omnicrafter crafting cheap Exarchs OR any other items by request :)

67
NoSoliciting/ContextMenu.cs Executable file
View File

@ -0,0 +1,67 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using CheapLoc;
using NoSoliciting.Interface;
using XivCommon.Functions;
namespace NoSoliciting {
public class ContextMenu : IDisposable {
private Plugin Plugin { get; }
internal ContextMenu(Plugin plugin) {
this.Plugin = plugin;
this.Plugin.Common.Functions.ContextMenu.OpenContextMenu += this.OnOpenContextMenu;
}
public void Dispose() {
this.Plugin.Common.Functions.ContextMenu.OpenContextMenu -= this.OnOpenContextMenu;
}
private void OnOpenContextMenu(ContextMenuArgs args) {
if (args.ParentAddonName != "LookingForGroup") {
return;
}
var label = Loc.Localize("ReportToNoSoliciting", "Report to NoSoliciting");
args.AdditionalItems.Add(new ContextMenuItem(label, this.Report));
// args.AdditionalItems.Add(new ContextMenuItem("Report to NoSoliciting", this.Report) {
// NameJapanese = "NoSolicitingへの報告",
// NameFrench = "Signaler à NoSoliciting",
// NameGerman = "An NoSoliciting melden",
// });
}
private void Report(ContextMenuItemSelectedArgs args) {
if (args.ContentIdLower == 0) {
return;
}
var listing = this.Plugin.Common.Functions.PartyFinder.CurrentListings
.Values
.FirstOrDefault(listing => listing.ContentIdLower == args.ContentIdLower);
if (listing == null) {
return;
}
var message = this.Plugin.PartyFinderHistory.FirstOrDefault(message => message.ActorId == listing.ContentIdLower);
if (message == null) {
return;
}
Task.Run(async () => {
var status = await this.Plugin.Ui.Report.ReportMessageAsync(message);
switch (status) {
case ReportStatus.Successful:
this.Plugin.Interface.Framework.Gui.Toast.ShowNormal($"Party Finder hosted by {listing.Name} reported successfully.");
break;
case ReportStatus.Failure:
this.Plugin.Interface.Framework.Gui.Toast.ShowError($"Failed to report Party Finder hosted by {listing.Name}.");
break;
}
});
}
}
}

View File

@ -1,275 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Dalamud.Game.Text;
using Dalamud.Plugin;
using NoSoliciting.Interface;
using NoSoliciting.Properties;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
namespace NoSoliciting {
public class Definitions {
public static string? LastError { get; private set; }
public static DateTime? LastUpdate { get; set; }
private const string Url = "https://git.sr.ht/~jkcclemens/NoSoliciting/blob/master/NoSoliciting/definitions.yaml";
public uint Version { get; private set; }
public Uri ReportUrl { get; private set; }
public Dictionary<string, Definition> Chat { get; private set; }
public Dictionary<string, Definition> PartyFinder { get; private set; }
public static async Task<Definitions> UpdateAndCache(Plugin plugin) {
#if DEBUG
return LoadDefaults();
#endif
Definitions? defs = null;
var download = await Download().ConfigureAwait(true);
if (download != null) {
defs = download.Item1;
try {
UpdateCache(plugin, download.Item2);
} catch (IOException e) {
PluginLog.Log("Could not update cache.");
PluginLog.Log(e.ToString());
}
}
return defs ?? await CacheOrDefault(plugin).ConfigureAwait(true);
}
public static Definitions Load(string text) {
var de = new DeserializerBuilder()
.WithNamingConvention(UnderscoredNamingConvention.Instance)
.WithTypeConverter(new MatcherConverter())
.IgnoreUnmatchedProperties()
.Build();
return de.Deserialize<Definitions>(text);
}
private static async Task<Definitions> CacheOrDefault(Plugin plugin) {
if (plugin == null) {
throw new ArgumentNullException(nameof(plugin), "Plugin cannot be null");
}
var pluginFolder = plugin.Interface.ConfigDirectory.ToString();
var cachedPath = Path.Combine(pluginFolder, "definitions.yaml");
if (!File.Exists(cachedPath)) {
goto LoadDefaults;
}
string text;
using (var file = File.OpenText(cachedPath)) {
text = await file.ReadToEndAsync().ConfigureAwait(true);
}
try {
return Load(text);
} catch (YamlException e) {
PluginLog.Log($"Could not load cached definitions: {e}. Loading defaults.");
}
LoadDefaults:
return LoadDefaults();
}
private static Definitions LoadDefaults() {
return Load(Resources.DefaultDefinitions);
}
private static async Task<Tuple<Definitions, string>?> Download() {
try {
using var client = new WebClient();
var text = await client.DownloadStringTaskAsync(Url).ConfigureAwait(true);
LastError = null;
return Tuple.Create(Load(text), text);
} catch (Exception e) when (e is WebException or YamlException) {
PluginLog.Log("Could not download newest definitions.");
PluginLog.Log(e.ToString());
LastError = e.Message;
return null;
}
}
private static async void UpdateCache(Plugin plugin, string defs) {
var pluginFolder = plugin.Interface.ConfigDirectory.ToString();
Directory.CreateDirectory(pluginFolder);
var cachePath = Path.Combine(pluginFolder, "definitions.yaml");
var b = Encoding.UTF8.GetBytes(defs);
using var file = File.OpenWrite(cachePath);
await file.WriteAsync(b, 0, b.Length).ConfigureAwait(true);
}
internal void Initialise(Plugin plugin) {
var defs = this.Chat.Select(e => new KeyValuePair<string, Definition>($"chat.{e.Key}", e.Value))
.Concat(this.PartyFinder.Select(e => new KeyValuePair<string, Definition>($"party_finder.{e.Key}", e.Value)));
foreach (var entry in defs) {
entry.Value.Initialise(entry.Key);
if (!plugin.Config.FilterStatus.TryGetValue(entry.Key, out _)) {
plugin.Config.FilterStatus[entry.Key] = entry.Value.Default;
}
}
plugin.Config.Save();
}
}
public class Definition {
private bool _initialised;
[YamlIgnore]
public string Id { get; private set; }
public List<List<Matcher>> RequiredMatchers { get; private set; } = new();
public List<List<Matcher>> LikelyMatchers { get; private set; } = new();
public int LikelihoodThreshold { get; private set; }
public bool IgnoreCase { get; private set; }
public bool Normalise { get; private set; } = true;
public List<XivChatType> Channels { get; private set; } = new();
public OptionNames Option { get; private set; }
public bool Default { get; private set; }
public void Initialise(string id) {
if (this._initialised) {
return;
}
this._initialised = true;
this.Id = id ?? throw new ArgumentNullException(nameof(id), "string cannot be null");
if (!this.IgnoreCase) {
return;
}
var allMatchers = this.LikelyMatchers
.Concat(this.RequiredMatchers)
.SelectMany(matchers => matchers);
foreach (var matcher in allMatchers) {
matcher.MakeIgnoreCase();
}
}
public bool Matches(XivChatType type, string text) {
if (text == null) {
throw new ArgumentNullException(nameof(text), "string cannot be null");
}
if (this.Channels.Count != 0 && !this.Channels.Contains(type)) {
return false;
}
if (this.Normalise) {
text = NoSolUtil.Normalise(text);
}
if (this.IgnoreCase) {
text = text.ToLowerInvariant();
}
// ensure all required matchers match
var allRequired = this.RequiredMatchers.All(matchers => matchers.Any(matcher => matcher.Matches(text)));
if (!allRequired) {
return false;
}
// calculate likelihood
var likelihood = this.LikelyMatchers.Count(matchers => matchers.Any(matcher => matcher.Matches(text)));
// matches only if likelihood is greater than or equal the threshold
return likelihood >= this.LikelihoodThreshold;
}
}
public class Matcher {
private string? substring;
private Regex? regex;
public Matcher(string substring) {
this.substring = substring ?? throw new ArgumentNullException(nameof(substring), "string cannot be null");
}
public Matcher(Regex regex) {
this.regex = regex ?? throw new ArgumentNullException(nameof(regex), "Regex cannot be null");
}
internal void MakeIgnoreCase() {
if (this.substring != null) {
this.substring = this.substring.ToLowerInvariant();
}
if (this.regex != null) {
this.regex = new Regex(this.regex.ToString(), this.regex.Options | RegexOptions.IgnoreCase);
}
}
public bool Matches(string text) {
if (text == null) {
throw new ArgumentNullException(nameof(text), "string cannot be null");
}
if (this.substring != null) {
return text.Contains(this.substring);
}
if (this.regex != null) {
return this.regex.IsMatch(text);
}
throw new ApplicationException("Matcher created without substring or regex");
}
}
public class OptionNames {
public string Basic { get; private set; }
public string Advanced { get; private set; }
}
internal sealed class MatcherConverter : IYamlTypeConverter {
public bool Accepts(Type type) {
return type == typeof(Matcher);
}
public object ReadYaml(IParser parser, Type type) {
Matcher matcher;
if (parser.TryConsume(out Scalar scalar)) {
matcher = new Matcher(scalar.Value);
} else if (parser.TryConsume(out MappingStart _)) {
if (parser.Consume<Scalar>().Value != "regex") {
throw new ArgumentException("matcher was an object but did not specify regex key");
}
var regex = new Regex(parser.Consume<Scalar>().Value, RegexOptions.Compiled);
matcher = new Matcher(regex);
parser.Consume<MappingEnd>();
} else {
throw new ArgumentException("invalid matcher");
}
return matcher;
}
public void WriteYaml(IEmitter emitter, object value, Type type) {
throw new NotImplementedException();
}
}
}

View File

@ -1,5 +1,4 @@
using Dalamud.Hooking;
using Dalamud.Plugin;
using Dalamud.Plugin;
using System;
using Dalamud.Game.Internal.Gui;
using Dalamud.Game.Internal.Gui.Structs;
@ -41,11 +40,7 @@ namespace NoSoliciting {
};
private Plugin Plugin { get; }
private bool _clearOnNext;
private delegate IntPtr HandlePfSummaryDelegate(IntPtr param1, IntPtr param2, byte param3);
private readonly Hook<HandlePfSummaryDelegate>? _handleSummaryHook;
private int LastBatch { get; set; } = -1;
private bool _disposedValue;
@ -54,11 +49,6 @@ namespace NoSoliciting {
this.Plugin.Interface.Framework.Gui.Chat.OnCheckMessageHandled += this.OnChat;
this.Plugin.Interface.Framework.Gui.PartyFinder.ReceiveListing += this.OnListing;
var summaryPtr = this.Plugin.Interface.TargetModuleScanner.ScanText("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8B FA 48 8B F1 45 84 C0 74 ?? 0F B7 0A");
this._handleSummaryHook = new Hook<HandlePfSummaryDelegate>(summaryPtr, new HandlePfSummaryDelegate(this.HandleSummary));
this._handleSummaryHook.Enable();
}
private void Dispose(bool disposing) {
@ -69,7 +59,6 @@ namespace NoSoliciting {
if (disposing) {
this.Plugin.Interface.Framework.Gui.Chat.OnCheckMessageHandled -= this.OnChat;
this.Plugin.Interface.Framework.Gui.PartyFinder.ReceiveListing -= this.OnListing;
this._handleSummaryHook?.Dispose();
}
this._disposedValue = true;
@ -82,25 +71,19 @@ namespace NoSoliciting {
}
private void OnChat(XivChatType type, uint senderId, ref SeString sender, ref SeString message, ref bool isHandled) {
isHandled = isHandled || this.FilterMessage(type, sender, message);
isHandled = isHandled || this.FilterMessage(type, senderId, sender, message);
}
private void OnListing(PartyFinderListing listing, PartyFinderListingEventArgs args) {
try {
if (this._clearOnNext) {
if (this.LastBatch != args.BatchNumber) {
this.Plugin.ClearPartyFinderHistory();
this._clearOnNext = false;
}
string? reason = null;
uint? version = null;
if (this.Plugin.MlReady) {
version = this.Plugin.MlFilter!.Version;
reason = this.MlListingFilterReason(listing);
} else if (this.Plugin.DefsReady) {
version = this.Plugin.Definitions!.Version;
reason = this.DefsListingFilterReason(listing);
}
this.LastBatch = args.BatchNumber;
var version = this.Plugin.MlFilter?.Version;
var reason = this.MlListingFilterReason(listing);
if (version == null) {
return;
@ -109,9 +92,10 @@ namespace NoSoliciting {
this.Plugin.AddPartyFinderHistory(new Message(
version.Value,
ChatType.None,
listing.ContentIdLower,
listing.Name,
listing.Description,
this.Plugin.MlReady,
true,
reason
));
@ -129,19 +113,15 @@ namespace NoSoliciting {
}
}
private bool FilterMessage(XivChatType type, SeString sender, SeString message) {
private bool FilterMessage(XivChatType type, uint senderId, SeString sender, SeString message) {
if (message == null) {
throw new ArgumentNullException(nameof(message), "SeString cannot be null");
}
if (this.Plugin.MlReady) {
return this.MlFilterMessage(type, sender, message);
}
return this.Plugin.DefsReady && this.DefsFilterMessage(type, sender, message);
return this.MlFilterMessage(type, senderId, sender, message);
}
private bool MlFilterMessage(XivChatType type, SeString sender, SeString message) {
private bool MlFilterMessage(XivChatType type, uint senderId, SeString sender, SeString message) {
if (this.Plugin.MlFilter == null) {
return false;
}
@ -177,6 +157,7 @@ namespace NoSoliciting {
this.Plugin.AddMessageHistory(new Message(
this.Plugin.MlFilter.Version,
ChatTypeExt.FromDalamud(type),
senderId,
sender,
message,
true,
@ -190,44 +171,6 @@ namespace NoSoliciting {
return filter;
}
private bool DefsFilterMessage(XivChatType type, SeString sender, SeString message) {
if (this.Plugin.Definitions == null || ChatTypeExt.FromDalamud(type).IsBattle()) {
return false;
}
var text = message.TextValue;
string? reason = null;
var filter = false;
foreach (var def in this.Plugin.Definitions.Chat.Values) {
filter = filter || this.Plugin.Config.FilterStatus.TryGetValue(def.Id, out var enabled)
&& enabled
&& def.Matches(type, text)
&& SetReason(out reason, def.Id);
}
// check for custom filters if enabled
filter = filter || this.Plugin.Config.CustomChatFilter
&& Chat.MatchesCustomFilters(text, this.Plugin.Config)
&& SetReason(out reason, "custom");
this.Plugin.AddMessageHistory(new Message(
this.Plugin.Definitions.Version,
ChatTypeExt.FromDalamud(type),
sender,
message,
false,
reason
));
if (filter && this.Plugin.Config.LogFilteredChat) {
PluginLog.Log($"Filtered chat message ({reason}): {text}");
}
return filter;
}
private string? MlListingFilterReason(PartyFinderListing listing) {
if (this.Plugin.MlFilter == null) {
return null;
@ -264,37 +207,6 @@ namespace NoSoliciting {
return null;
}
private string? DefsListingFilterReason(PartyFinderListing listing) {
if (this.Plugin.Definitions == null) {
return null;
}
var desc = listing.Description.TextValue;
if (this.Plugin.Config.FilterHugeItemLevelPFs && listing.MinimumItemLevel > FilterUtil.MaxItemLevelAttainable(this.Plugin.Interface.Data)) {
return "ilvl";
}
foreach (var def in this.Plugin.Definitions.PartyFinder.Values) {
if (this.Plugin.Config.FilterStatus.TryGetValue(def.Id, out var enabled) && enabled && def.Matches(XivChatType.None, desc)) {
return def.Id;
}
}
// check for custom filters if enabled
if (this.Plugin.Config.CustomPFFilter && PartyFinder.MatchesCustomFilters(desc, this.Plugin.Config)) {
return "custom";
}
return null;
}
private IntPtr HandleSummary(IntPtr param1, IntPtr param2, byte param3) {
this._clearOnNext = true;
return this._handleSummaryHook!.Original(param1, param2, param3);
}
private static bool SetReason(out string reason, string value) {
reason = value;
return true;

View File

@ -9,6 +9,7 @@
<InputAssemblies Include="$(OutputPath)\M*.dll"/>
<InputAssemblies Include="$(OutputPath)\S*.dll"/>
<InputAssemblies Include="$(OutputPath)\Y*.dll"/>
<InputAssemblies Include="$(OutputPath)\XivCommon.dll"/>
</ItemGroup>
<ILRepack
Parallel="true"

View File

@ -232,24 +232,7 @@ namespace NoSoliciting.Interface {
ImGui.PopStyleColor();
} else {
if (ImGui.Button("Report")) {
Task.Run(async () => {
string? resp = null;
try {
using var client = new WebClient();
this.LastReportStatus = ReportStatus.InProgress;
var reportUrl = this.Plugin.MlFilter?.ReportUrl;
if (reportUrl != null) {
resp = await client.UploadStringTaskAsync(reportUrl, message.ToJson()).ConfigureAwait(true);
}
} catch (Exception) {
// ignored
}
this.LastReportStatus = resp == "{\"message\":\"ok\"}" ? ReportStatus.Successful : ReportStatus.Failure;
PluginLog.Log(resp == null
? "Report not sent. ML model not set."
: $"Report sent. Response: {resp}");
});
this.ReportMessage(message);
ImGui.CloseCurrentPopup();
}
@ -308,6 +291,36 @@ namespace NoSoliciting.Interface {
return clicked;
}
internal void ReportMessage(Message message) {
Task.Run(async () => await this.ReportMessageAsync(message));
}
internal async Task<ReportStatus> ReportMessageAsync(Message message) {
string? resp = null;
try {
using var client = new WebClient();
this.LastReportStatus = ReportStatus.InProgress;
var reportUrl = this.Plugin.MlFilter?.ReportUrl;
if (reportUrl != null) {
resp = await client.UploadStringTaskAsync(reportUrl, message.ToJson()).ConfigureAwait(true);
}
} catch (Exception) {
// ignored
}
var status = resp == "{\"message\":\"ok\"}" ? ReportStatus.Successful : ReportStatus.Failure;
if (status == ReportStatus.Failure) {
PluginLog.LogWarning($"Failed to report message:\n{resp}");
}
this.LastReportStatus = status;
PluginLog.Log(resp == null
? "Report not sent. ML model not set."
: $"Report sent. Response: {resp}");
return status;
}
#endregion
}
}

View File

@ -1,8 +1,11 @@
using System;
using System.Collections.Generic;
using System.Numerics;
using System.Reflection;
using System.Text.RegularExpressions;
using CheapLoc;
using Dalamud.Interface;
using Dalamud.Plugin;
using ImGuiNET;
using NoSoliciting.Ml;
@ -38,26 +41,13 @@ namespace NoSoliciting.Interface {
}
public void Draw() {
if (!this.ShowSettings || !ImGui.Begin($"{this.Plugin.Name} settings", ref this._showSettings)) {
var windowTitle = Loc.Localize("Settings", $"{this.Plugin.Name} settings");
if (!this.ShowSettings || !ImGui.Begin($"{windowTitle}###NoSoliciting settings", ref this._showSettings)) {
return;
}
var modes = new[] {
"Machine learning (default)",
"Definition matchers (obsolete)",
};
var modeIndex = this.Plugin.Config.UseMachineLearning ? 0 : 1;
if (ImGui.Combo("Filter mode", ref modeIndex, modes, modes.Length)) {
this.Plugin.Config.UseMachineLearning = modeIndex == 0;
this.Plugin.Config.Save();
if (this.Plugin.Config.UseMachineLearning) {
this.Plugin.InitialiseMachineLearning(false);
}
}
var advanced = this.Plugin.Config.AdvancedMode;
if (ImGui.Checkbox("Advanced mode", ref advanced)) {
if (ImGui.Checkbox(Loc.Localize("AdvancedMode", "Advanced mode"), ref advanced)) {
this.Plugin.Config.AdvancedMode = advanced;
this.Plugin.Config.Save();
}
@ -68,23 +58,19 @@ namespace NoSoliciting.Interface {
return;
}
if (this.Plugin.Config.UseMachineLearning) {
this.DrawMachineLearningConfig();
} else {
this.DrawDefinitionsConfig();
}
this.DrawMachineLearningConfig();
this.DrawOtherFilters();
if (ImGui.BeginTabItem("Other")) {
if (ImGui.BeginTabItem(Loc.Localize("OtherTab", "Other"))) {
var logFilteredPfs = this.Plugin.Config.LogFilteredPfs;
if (ImGui.Checkbox("Log filtered PFs", ref logFilteredPfs)) {
if (ImGui.Checkbox(Loc.Localize("LogFilteredPfs", "Log filtered PFs"), ref logFilteredPfs)) {
this.Plugin.Config.LogFilteredPfs = logFilteredPfs;
this.Plugin.Config.Save();
}
var logFilteredMessages = this.Plugin.Config.LogFilteredChat;
if (ImGui.Checkbox("Log filtered messages", ref logFilteredMessages)) {
if (ImGui.Checkbox(Loc.Localize("LogFilteredMessages", "Log filtered messages"), ref logFilteredMessages)) {
this.Plugin.Config.LogFilteredChat = logFilteredMessages;
this.Plugin.Config.Save();
}
@ -96,7 +82,7 @@ namespace NoSoliciting.Interface {
ImGui.Separator();
if (ImGui.Button("Show reporting window")) {
if (ImGui.Button(Loc.Localize("ShowReportingWindow", "Show reporting window"))) {
this.Ui.Report.Open();
}
@ -112,7 +98,7 @@ namespace NoSoliciting.Interface {
this.DrawBasicMachineLearningConfig();
}
if (!ImGui.BeginTabItem("Model")) {
if (!ImGui.BeginTabItem(Loc.Localize("ModelTab", "Model"))) {
return;
}
@ -123,7 +109,7 @@ namespace NoSoliciting.Interface {
ImGui.TextUnformatted($"Last error: {lastError}");
}
if (ImGui.Button("Update model")) {
if (ImGui.Button(Loc.Localize("UpdateModel", "Update model"))) {
// prevent issues when people spam the button
if (ImGui.GetIO().KeyCtrl || this.Plugin.MlStatus is MlFilterStatus.Uninitialised or MlFilterStatus.Initialised) {
this.Plugin.MlFilter?.Dispose();
@ -137,7 +123,7 @@ namespace NoSoliciting.Interface {
}
private void DrawBasicMachineLearningConfig() {
if (!ImGui.BeginTabItem("Filters")) {
if (!ImGui.BeginTabItem(Loc.Localize("FiltersTab", "Filters"))) {
return;
}
@ -168,13 +154,13 @@ namespace NoSoliciting.Interface {
}
private void DrawAdvancedMachineLearningConfig() {
if (!ImGui.BeginTabItem("Filters")) {
if (!ImGui.BeginTabItem(Loc.Localize("FiltersTab", "Filters"))) {
return;
}
ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(255f, 204f, 0f, 1f));
ImGui.TextUnformatted("Do not change advanced settings unless you know what you are doing.");
ImGui.TextUnformatted("The machine learning model was trained with certain channels in mind.");
ImGui.TextUnformatted(Loc.Localize("AdvancedWarning1", "Do not change advanced settings unless you know what you are doing."));
ImGui.TextUnformatted(Loc.Localize("AdvancedWarning2", "The machine learning model was trained with certain channels in mind."));
ImGui.PopStyleColor();
foreach (var category in MessageCategoryExt.UiOrder) {
@ -217,91 +203,16 @@ namespace NoSoliciting.Interface {
#endregion
#region Definitions config
private void DrawDefinitionsConfig() {
if (this.Plugin.Config.AdvancedMode) {
this.DrawDefsAdvancedSettings();
} else {
this.DrawDefsBasicSettings();
}
this.DrawDefinitionsTab();
}
private void DrawDefinitionsTab() {
if (!ImGui.BeginTabItem("Definitions")) {
return;
}
if (this.Plugin.Definitions != null) {
ImGui.TextUnformatted($"Version: {this.Plugin.Definitions.Version}");
}
if (Definitions.LastUpdate != null) {
ImGui.TextUnformatted($"Last update: {Definitions.LastUpdate}");
}
var error = Definitions.LastError;
if (error != null) {
ImGui.TextUnformatted($"Last error: {error}");
}
if (ImGui.Button("Update definitions")) {
this.Plugin.UpdateDefinitions();
}
ImGui.EndTabItem();
}
private void DrawDefsBasicSettings() {
if (this.Plugin.Definitions == null) {
return;
}
if (!ImGui.BeginTabItem("Filters")) {
return;
}
this.DrawCheckboxes(this.Plugin.Definitions.Chat.Values, true, "chat");
ImGui.Separator();
this.DrawCheckboxes(this.Plugin.Definitions.PartyFinder.Values, true, "Party Finder");
ImGui.EndTabItem();
}
private void DrawDefsAdvancedSettings() {
if (this.Plugin.Definitions == null) {
return;
}
if (ImGui.BeginTabItem("Chat")) {
this.DrawCheckboxes(this.Plugin.Definitions.Chat.Values, false, "chat");
ImGui.EndTabItem();
}
if (ImGui.BeginTabItem("Party Finder")) {
this.DrawCheckboxes(this.Plugin.Definitions.PartyFinder.Values, false, "Party Finder");
ImGui.EndTabItem();
}
}
#endregion
#region Other config
private void DrawOtherFilters() {
if (!ImGui.BeginTabItem("Other filters")) {
if (!ImGui.BeginTabItem(Loc.Localize("OtherFiltersTab", "Other filters"))) {
return;
}
if (ImGui.CollapsingHeader("Chat filters")) {
if (ImGui.CollapsingHeader(Loc.Localize("ChatFilters", "Chat filters"))) {
var customChat = this.Plugin.Config.CustomChatFilter;
if (ImGui.Checkbox("Enable custom chat filters", ref customChat)) {
if (ImGui.Checkbox(Loc.Localize("EnableCustomChatFilters", "Enable custom chat filters"), ref customChat)) {
this.Plugin.Config.CustomChatFilter = customChat;
this.Plugin.Config.Save();
}
@ -313,22 +224,22 @@ namespace NoSoliciting.Interface {
}
}
if (ImGui.CollapsingHeader("Party Finder filters")) {
if (ImGui.CollapsingHeader(Loc.Localize("PartyFinderFilters", "Party Finder filters"))) {
var filterHugeItemLevelPFs = this.Plugin.Config.FilterHugeItemLevelPFs;
// ReSharper disable once InvertIf
if (ImGui.Checkbox("Filter PFs with item level above maximum", ref filterHugeItemLevelPFs)) {
if (ImGui.Checkbox(Loc.Localize("FilterIlvlPfs", "Filter PFs with item level above maximum"), ref filterHugeItemLevelPFs)) {
this.Plugin.Config.FilterHugeItemLevelPFs = filterHugeItemLevelPFs;
this.Plugin.Config.Save();
}
var considerPrivate = this.Plugin.Config.ConsiderPrivatePfs;
if (ImGui.Checkbox("Apply filters to private Party Finder listings", ref considerPrivate)) {
if (ImGui.Checkbox(Loc.Localize("FilterPrivatePfs", "Apply filters to private Party Finder listings"), ref considerPrivate)) {
this.Plugin.Config.ConsiderPrivatePfs = considerPrivate;
this.Plugin.Config.Save();
}
var customPf = this.Plugin.Config.CustomPFFilter;
if (ImGui.Checkbox("Enable custom Party Finder filters", ref customPf)) {
if (ImGui.Checkbox(Loc.Localize("EnableCustomPartyFinderFilters", "Enabled custom Party Finder filters"), ref customPf)) {
this.Plugin.Config.CustomPFFilter = customPf;
this.Plugin.Config.Save();
}
@ -346,7 +257,7 @@ namespace NoSoliciting.Interface {
private void DrawCustom(string name, ref List<string> substrings, ref List<string> regexes) {
ImGui.Columns(2);
ImGui.TextUnformatted("Substrings to filter");
ImGui.TextUnformatted(Loc.Localize("SubstringsToFilter", "Substrings to filter"));
if (ImGui.BeginChild($"##{name}-substrings", new Vector2(0, 175))) {
for (var i = 0; i < substrings.Count; i++) {
var input = substrings[i];
@ -375,7 +286,7 @@ namespace NoSoliciting.Interface {
ImGui.NextColumn();
ImGui.TextUnformatted("Regular expressions to filter");
ImGui.TextUnformatted(Loc.Localize("RegularExpressionsToFilter", "Regular expressions to filter"));
if (ImGui.BeginChild($"##{name}-regexes", new Vector2(0, 175))) {
for (var i = 0; i < regexes.Count; i++) {
var input = regexes[i];
@ -411,29 +322,13 @@ namespace NoSoliciting.Interface {
ImGui.Columns(1);
// ReSharper disable once InvertIf
if (ImGui.Button($"Save filters##{name}-save")) {
var saveLoc = Loc.Localize("SaveFilters", "Save filters");
if (ImGui.Button($"{saveLoc}##{name}-save")) {
this.Plugin.Config.Save();
this.Plugin.Config.CompileRegexes();
}
}
#endregion
#region Utility
private void DrawCheckboxes(IEnumerable<Definition> defs, bool basic, string labelFillIn) {
foreach (var def in defs) {
this.Plugin.Config.FilterStatus.TryGetValue(def.Id, out var enabled);
var label = basic ? def.Option.Basic : def.Option.Advanced;
label = label.Replace("{}", labelFillIn);
// ReSharper disable once InvertIf
if (ImGui.Checkbox(label, ref enabled)) {
this.Plugin.Config.FilterStatus[def.Id] = enabled;
this.Plugin.Config.Save();
}
}
}
#endregion
}
}

View File

@ -14,8 +14,11 @@ using NoSoliciting.Ml;
#endif
namespace NoSoliciting {
[Serializable]
public class Message {
public Guid Id { get; }
[JsonIgnore]
public uint ActorId { get; }
public uint DefinitionsVersion { get; }
public DateTime Timestamp { get; }
public ChatType ChatType { get; }
@ -24,27 +27,18 @@ namespace NoSoliciting {
public bool Ml { get; }
public string? FilterReason { get; }
public Message(uint defsVersion, ChatType type, SeString sender, SeString content, bool ml, string? reason) {
public Message(uint defsVersion, ChatType type, uint actorId, SeString sender, SeString content, bool ml, string? reason) {
this.Id = Guid.NewGuid();
this.DefinitionsVersion = defsVersion;
this.Timestamp = DateTime.Now;
this.ChatType = type;
this.ActorId = actorId;
this.Sender = sender;
this.Content = content;
this.Ml = ml;
this.FilterReason = reason;
}
public Message(uint defsVersion, ChatType type, string sender, string content, bool ml, string? reason) : this(
defsVersion,
type,
new SeString(new Payload[] {new TextPayload(sender)}),
new SeString(new Payload[] {new TextPayload(content)}),
ml,
reason
) {
}
[Serializable]
[JsonObject(NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
private class JsonMessage {

View File

@ -1,4 +1,6 @@
using System;
using CheapLoc;
using Dalamud;
namespace NoSoliciting.Ml {
public enum MessageCategory {
@ -42,7 +44,6 @@ namespace NoSoliciting.Ml {
};
#if DEBUG
public static string ToModelName(this MessageCategory category) => category switch {
MessageCategory.Trade => "TRADE",
MessageCategory.FreeCompany => "FC",
@ -74,30 +75,30 @@ namespace NoSoliciting.Ml {
#endif
public static string Name(this MessageCategory category) => category switch {
MessageCategory.Trade => "Trade ads",
MessageCategory.FreeCompany => "Free Company ads",
MessageCategory.Normal => "Normal messages",
MessageCategory.Phishing => "Phishing messages",
MessageCategory.RmtContent => "RMT (content)",
MessageCategory.RmtGil => "RMT (gil)",
MessageCategory.Roleplaying => "Roleplaying ads",
MessageCategory.Static => "Static recruitment",
MessageCategory.Community => "Community ads",
MessageCategory.StaticSub => "Static substitutes",
MessageCategory.Trade => Loc.Localize("TradeCategory", "Trade ads"),
MessageCategory.FreeCompany => Loc.Localize("FreeCompanyCategory", "Free Company ads"),
MessageCategory.Normal => Loc.Localize("NormalCategory", "Normal messages"),
MessageCategory.Phishing => Loc.Localize("PhishingCategory", "Phishing messages"),
MessageCategory.RmtContent => Loc.Localize("RmtContentCategory", "RMT (content)"),
MessageCategory.RmtGil => Loc.Localize("RmtGilCategory", "RMT (gil)"),
MessageCategory.Roleplaying => Loc.Localize("RoleplayingCategory", "Roleplaying ads"),
MessageCategory.Static => Loc.Localize("StaticCategory", "Static recruitment"),
MessageCategory.Community => Loc.Localize("CommunityCategory", "Community ads"),
MessageCategory.StaticSub => Loc.Localize("StaticSubCategory", "Static substitutes"),
_ => throw new ArgumentException("Invalid category", nameof(category)),
};
public static string Description(this MessageCategory category) => category switch {
MessageCategory.Trade => "Messages advertising trading items or services for gil, such as omnicrafters looking for work or people selling rare items off the market board",
MessageCategory.FreeCompany => "Advertisements for Free Companies",
MessageCategory.Normal => "Normal messages that should not be filtered",
MessageCategory.Phishing => "Messages trying to trick you into revealing your account details in order to steal your account",
MessageCategory.RmtContent => "Real-money trade involving content (also known as content sellers)",
MessageCategory.RmtGil => "Real-money trade involving gil or items (also known as RMT bots)",
MessageCategory.Roleplaying => "Advertisements for personal RP, RP communities, venues, or anything else related to roleplaying",
MessageCategory.Static => "Statics looking for members or players looking for a static",
MessageCategory.Community => "Advertisements for general-purpose communities, generally Discord servers",
MessageCategory.StaticSub => "Statics looking for fill-ins of missing members for clears",
MessageCategory.Trade => Loc.Localize("TradeDescription", "Messages advertising trading items or services for gil, such as omnicrafters looking for work or people selling rare items off the market board"),
MessageCategory.FreeCompany => Loc.Localize("FreeCompanyDescription", "Advertisements for Free Companies"),
MessageCategory.Normal => Loc.Localize("NormalDescription", "Normal messages that should not be filtered"),
MessageCategory.Phishing => Loc.Localize("PhishingDescription", "Messages trying to trick you into revealing your account details in order to steal your account"),
MessageCategory.RmtContent => Loc.Localize("RmtContentDescription", "Real-money trade involving content (also known as content sellers)"),
MessageCategory.RmtGil => Loc.Localize("RmtGilDescription", "Real-money trade involving gil or items (also known as RMT bots)"),
MessageCategory.Roleplaying => Loc.Localize("RoleplayingDescription", "Advertisements for personal RP, RP communities, venues, or anything else related to roleplaying"),
MessageCategory.Static => Loc.Localize("StaticDescription", "Statics looking for members or players looking for a static"),
MessageCategory.Community => Loc.Localize("CommunityDescription", "Advertisements for general-purpose communities, generally Discord servers"),
MessageCategory.StaticSub => Loc.Localize("StaticSubDescription", "Statics looking for fill-ins of missing members for clears"),
_ => throw new ArgumentException("Invalid category", nameof(category)),
};
}

View File

@ -9,6 +9,10 @@
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<ItemGroup>
<Reference Include="CheapLoc">
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\dev\CheapLoc.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Dalamud">
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\dev\Dalamud.dll</HintPath>
<Private>False</Private>
@ -33,6 +37,9 @@
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\dev\Newtonsoft.Json.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="XivCommon, Version=1.4.0.0, Culture=neutral, PublicKeyToken=null">
<HintPath>..\..\XivCommon\XivCommon\bin\Release\net48\XivCommon.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="DalamudPackager" Version="1.2.1"/>
@ -44,18 +51,8 @@
<PackageReference Include="YamlDotNet" Version="11.1.1"/>
</ItemGroup>
<ItemGroup>
<Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Include="..\NoSoliciting.MessageClassifier\bin\Release\net48\NoSoliciting.MessageClassifier.exe"/>
<EmbeddedResource Include="Resources\en.json"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\NoSoliciting.Interface\NoSoliciting.Interface.csproj"/>

View File

@ -4,8 +4,11 @@ using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Threading.Tasks;
using CheapLoc;
using Dalamud;
using NoSoliciting.Interface;
using NoSoliciting.Ml;
using XivCommon;
namespace NoSoliciting {
public class Plugin : IDalamudPlugin {
@ -17,13 +20,12 @@ namespace NoSoliciting {
public DalamudPluginInterface Interface { get; private set; } = null!;
public PluginConfiguration Config { get; private set; } = null!;
public XivCommonBase Common { get; private set; } = null!;
public PluginUi Ui { get; private set; } = null!;
public Commands Commands { get; private set; } = null!;
public Definitions? Definitions { get; private set; }
private ContextMenu ContextMenu { get; set; } = null!;
public MlFilterStatus MlStatus { get; set; } = MlFilterStatus.Uninitialised;
public MlFilter? MlFilter { get; set; }
public bool MlReady => this.Config.UseMachineLearning && this.MlFilter != null;
public bool DefsReady => !this.Config.UseMachineLearning && this.Definitions != null;
private readonly List<Message> _messageHistory = new();
public IEnumerable<Message> MessageHistory => this._messageHistory;
@ -42,19 +44,20 @@ namespace NoSoliciting {
this.Interface = pluginInterface;
Loc.Setup(Resourcer.Resource.AsString("Resources/en.json"), Assembly.GetAssembly(typeof(Plugin)));
this.Config = this.Interface.GetPluginConfig() as PluginConfiguration ?? new PluginConfiguration();
this.Config.Initialise(this.Interface);
this.Common = new XivCommonBase(this.Interface, Hooks.PartyFinder | Hooks.ContextMenu);
this.Ui = new PluginUi(this);
this.Commands = new Commands(this);
this.UpdateDefinitions();
this.ContextMenu = new ContextMenu(this);
this.Filter = new Filter(this);
if (this.Config.UseMachineLearning) {
this.InitialiseMachineLearning(false);
}
this.InitialiseMachineLearning(false);
// pre-compute the max ilvl to prevent stutter
try {
@ -70,10 +73,14 @@ namespace NoSoliciting {
}
if (disposing) {
Loc.ExportLocalizable();
this.Filter.Dispose();
this.MlFilter?.Dispose();
this.ContextMenu.Dispose();
this.Commands.Dispose();
this.Ui.Dispose();
this.Common.Dispose();
}
this._disposedValue = true;
@ -96,18 +103,6 @@ namespace NoSoliciting {
});
}
internal void UpdateDefinitions() {
Task.Run(async () => {
var defs = await Definitions.UpdateAndCache(this).ConfigureAwait(true);
// this shouldn't be possible, but what do I know
if (defs != null) {
defs.Initialise(this);
this.Definitions = defs;
Definitions.LastUpdate = DateTime.Now;
}
});
}
public void AddMessageHistory(Message message) {
this._messageHistory.Insert(0, message);

View File

@ -51,8 +51,6 @@ namespace NoSoliciting {
public bool FilterHugeItemLevelPFs { get; set; }
public bool UseMachineLearning { get; set; } = true;
public HashSet<MessageCategory> BasicMlFilters { get; set; } = new() {
MessageCategory.RmtGil,
MessageCategory.RmtContent,

View File

@ -1,84 +0,0 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace NoSoliciting.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("NoSoliciting.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to # This file defines the filters that NoSoliciting will use for
///# built-in filters.
///
///# The version should be incremented for each commit including changes
///# to this file.
///
///# There are three main sections: chat, party_finder, and global. The
///# chat and party_finder sections are for their respective areas (the
///# chat log and the Party Finder window), and the global section
///# applies to both.
///
///# Each subsection is a separate built-in filter that can be toggled on
///# and off. The option shown in the UI is defined [rest of string was truncated]&quot;;.
/// </summary>
internal static string DefaultDefinitions {
get {
return ResourceManager.GetString("DefaultDefinitions", resourceCulture);
}
}
}
}

View File

@ -1,124 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="DefaultDefinitions" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\definitions.yaml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
</data>
</root>

170
NoSoliciting/Resources/en.json Executable file
View File

@ -0,0 +1,170 @@
{
"ReportToNoSoliciting": {
"message": "Report to NoSoliciting",
"description": "ContextMenu.OnOpenContextMenu"
},
"TradeCategory": {
"message": "Trade ads",
"description": "MessageCategoryExt.Name"
},
"FreeCompanyCategory": {
"message": "Free Company ads",
"description": "MessageCategoryExt.Name"
},
"NormalCategory": {
"message": "Normal messages",
"description": "MessageCategoryExt.Name"
},
"PhishingCategory": {
"message": "Phishing messages",
"description": "MessageCategoryExt.Name"
},
"RmtContentCategory": {
"message": "RMT (content)",
"description": "MessageCategoryExt.Name"
},
"RmtGilCategory": {
"message": "RMT (gil)",
"description": "MessageCategoryExt.Name"
},
"RoleplayingCategory": {
"message": "Roleplaying ads",
"description": "MessageCategoryExt.Name"
},
"StaticCategory": {
"message": "Static recruitment",
"description": "MessageCategoryExt.Name"
},
"CommunityCategory": {
"message": "Community ads",
"description": "MessageCategoryExt.Name"
},
"StaticSubCategory": {
"message": "Static substitutes",
"description": "MessageCategoryExt.Name"
},
"TradeDescription": {
"message": "Messages advertising trading items or services for gil, such as omnicrafters looking for work or people selling rare items off the market board",
"description": "MessageCategoryExt.Description"
},
"FreeCompanyDescription": {
"message": "Advertisements for Free Companies",
"description": "MessageCategoryExt.Description"
},
"NormalDescription": {
"message": "Normal messages that should not be filtered",
"description": "MessageCategoryExt.Description"
},
"PhishingDescription": {
"message": "Messages trying to trick you into revealing your account details in order to steal your account",
"description": "MessageCategoryExt.Description"
},
"RmtContentDescription": {
"message": "Real-money trade involving content (also known as content sellers)",
"description": "MessageCategoryExt.Description"
},
"RmtGilDescription": {
"message": "Real-money trade involving gil or items (also known as RMT bots)",
"description": "MessageCategoryExt.Description"
},
"RoleplayingDescription": {
"message": "Advertisements for personal RP, RP communities, venues, or anything else related to roleplaying",
"description": "MessageCategoryExt.Description"
},
"StaticDescription": {
"message": "Statics looking for members or players looking for a static",
"description": "MessageCategoryExt.Description"
},
"CommunityDescription": {
"message": "Advertisements for general-purpose communities, generally Discord servers",
"description": "MessageCategoryExt.Description"
},
"StaticSubDescription": {
"message": "Statics looking for fill-ins of missing members for clears",
"description": "MessageCategoryExt.Description"
},
" settings": {
"message": null,
"description": "Settings.Draw"
},
"AdvancedMode": {
"message": "Advanced mode",
"description": "Settings.Draw"
},
"OtherTab": {
"message": "Other",
"description": "Settings.Draw"
},
"LogFilteredPfs": {
"message": "Log filtered PFs",
"description": "Settings.Draw"
},
"LogFilteredMessages": {
"message": "Log filtered messages",
"description": "Settings.Draw"
},
"ShowReportingWindow": {
"message": "Show reporting window",
"description": "Settings.Draw"
},
"ModelTab": {
"message": "Model",
"description": "Settings.DrawMachineLearningConfig"
},
"UpdateModel": {
"message": "Update model",
"description": "Settings.DrawMachineLearningConfig"
},
"FiltersTab": {
"message": "Filters",
"description": "Settings.DrawBasicMachineLearningConfig"
},
"AdvancedWarning1": {
"message": "Do not change advanced settings unless you know what you are doing.",
"description": "Settings.DrawAdvancedMachineLearningConfig"
},
"AdvancedWarning2": {
"message": "The machine learning model was trained with certain channels in mind.",
"description": "Settings.DrawAdvancedMachineLearningConfig"
},
"OtherFiltersTab": {
"message": "Other filters",
"description": "Settings.DrawOtherFilters"
},
"ChatFilters": {
"message": "Chat filters",
"description": "Settings.DrawOtherFilters"
},
"EnableCustomChatFilters": {
"message": "Enable custom chat filters",
"description": "Settings.DrawOtherFilters"
},
"PartyFinderFilters": {
"message": "Party Finder filters",
"description": "Settings.DrawOtherFilters"
},
"FilterIlvlPfs": {
"message": "Filter PFs with item level above maximum",
"description": "Settings.DrawOtherFilters"
},
"FilterPrivatePfs": {
"message": "Apply filters to private Party Finder listings",
"description": "Settings.DrawOtherFilters"
},
"EnableCustomPartyFinderFilters": {
"message": "Enabled custom Party Finder filters",
"description": "Settings.DrawOtherFilters"
},
"SubstringsToFilter": {
"message": "Substrings to filter",
"description": "Settings.DrawCustom"
},
"RegularExpressionsToFilter": {
"message": "Regular expressions to filter",
"description": "Settings.DrawCustom"
},
"SaveFilters": {
"message": "Save filters",
"description": "Settings.DrawCustom"
}
}

View File

@ -1,150 +0,0 @@
# THIS FILE IS DEPRECATED AND WILL NO LONGER RECEIVE UPDATES. Please
# see the file called data.csv in NoSoliciting.Trainer for how to
# update the new machine learning model that has replaced this file.
# This file defines the filters that NoSoliciting will use for
# built-in filters.
# The version should be incremented for each commit including changes
# to this file.
# There are two main sections: chat and party_finder. The chat and
# party_finder sections are for their respective areas (the chat log
# and the Party Finder window).
# Each subsection is a separate built-in filter that can be toggled on
# and off. The option shown in the UI is defined in the subsection.
# Subsections can have ignore_case (defaults to false) and normalise
# (defaults to true) set. ignore_case will ignore casing for matching
# against the matchers, and normalise will normalise text prior to
# matching. Text normalisation consists of turning FFXIV-specific
# unicode symbols into normal ASCII characters and running a NFKD
# unicode decomposition on the result.
# Subsections also may filter based on channels with the channels key.
# A list of channels may be specified, and the message will be ignored
# if not in one of the specified channels. For the Party Finder, the
# channel is always None. An empty list (or missing channels key) will
# ignore the channel.
# Each subsection may specify whether it is enabled by default with
# the default key. This should be used sparingly. This defaults to
# false.
# The real meat of the file is the matchers. There are two types of
# matchers: required and likely. Both types have categories of strings
# or regular expressions that should match. For required matchers, at
# least one string or regex should match in *all* categories. For
# likely matchers, at least one string or regex should match in the
# value of likelihood_threshold (or greater) categories.
# If both required and likely matchers are specified, they both must
# match. This means that all the categories of the required matchers
# must find a match, *and* that at least likelihood_threshold matchers
# must find a match in likely_matchers.
# Substring matchers are faster than regular expressions and are
# specified just by using a string. Regular expression matchers are
# slower but more flexible, and they are specified by using a regex
# key, as can be seen below.
version: 68
# This is the URL the plugin will POST reports to.
report_url: https://nosol.annaclemens.io/report
chat:
rmt:
option:
basic: Filter RMT from chat
advanced: Enable built-in chat RMT filter
default: true
required_matchers:
- - 4KGOLD
- We have sufficient stock
- PVPBANK.COM
- Gil for free
- www.so9.com
- Fast & Convenient
- Cheap & Safety Guarantee
- 【Code|A O A U E
- igfans
- 4KGOLD.COM
- Cheapest Gil with
- pvp and bank on google
- Selling Cheap GIL
- ff14mogstation.com
- Cheap Gil 1000k
- gilsforyou
- server 1000K =
- gils_selling
- E A S Y.C O M
- bonus code
- mins delivery guarantee
- Sell cheap
- Salegm.com
- cheap Mog
- Off Code
- FF14Mog.com
- ff14mog.com
- 使用する5
- 5分納品
- offers Fantasia
- 5GOLD.COM
- Buy Cheap gils
- regex: '【 .+ 、. = .+ 】'
- regex: 'finalfantasyxiv\.com-\w+\.\w+' # phishing
- regex: '\.com-\w+\.\w+/' # phishing
- regex: '(?i)giveaway of \d+.?m.*discord.gg/.' # phishing
party_finder:
rmt:
option:
basic: Filter RMT from Party Finder
advanced: Enable built-in Party Finder RMT filter
default: true
ignore_case: true
required_matchers:
# selling
- - sell
- s e l l
- $ell
- $ e l l
- sale
- s a l e
- price
- cheap
- gil only
# rmt groups
- minmax
- lalakuza
- legacy
- sk7
- restocker
- 'fast&easy'
- fast and easy help
- '[helping]'
- lawyer
# more keywords
- deliver
- best team
- satisfaction
- coaching
- reliable
- regex: '\s#1'
- regex: '\bi\d{3}.+loot\b'
- regex: 'm\s*i\s*n\s*m\s*a\s*x'
- regex: 'pre.?order'
- regex: '\boffer'
# content
- - eden
- savage
- primal
- ultimate
- trial
- loot
- content
- ucob
- regex: '\bex\b'
- regex: e[1-9][0-2]?