m_uniquemsg.c 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. /* Copyright (C) All Rights Reserved
  2. ** Written by Gottem <support@gottem.nl>
  3. ** Website: https://gitgud.malvager.net/Wazakindjes/unrealircd_mods
  4. ** License: https://gitgud.malvager.net/Wazakindjes/unrealircd_mods/raw/master/LICENSE
  5. */
  6. // One include for all cross-platform compatibility thangs
  7. #include "unrealircd.h"
  8. #define CHMODE_FLAG 'U'
  9. // Dem macros yo
  10. #define IsUMsg(x) ((x) && (x)->mode.extmode & uniquemsg_extcmode)
  11. #define IsMDErr(x, y, z) \
  12. do { \
  13. if(!(x)) { \
  14. config_error("A critical error occurred when registering ModData for %s: %s", MOD_HEADER(y).name, ModuleGetErrorStr((z)->handle)); \
  15. return MOD_FAILED; \
  16. } \
  17. } while(0)
  18. // Quality fowod declarations
  19. int is_chanowner_prot(aClient *sptr, aChannel *chptr);
  20. int wasRepeat(aChannel *chptr, char *text);
  21. void uniquemsg_md_free(ModData *md);
  22. int uniquemsg_chmode_isok(aClient *sptr, aChannel *chptr, char mode, char *para, int checkt, int what);
  23. char *uniquemsg_hook_prechanmsg(aClient *sptr, aChannel *chptr, char *text, int notice);
  24. // Muh globals
  25. static ModuleInfo *uniquemsgMI = NULL; // Store ModuleInfo so we can use it to check for errors in MOD_LOAD
  26. Cmode_t uniquemsg_extcmode = 0L; // Store bitwise value latur
  27. ModDataInfo *umsgMDI; // To store some shit with the channel ;]
  28. // Dat dere module header
  29. ModuleHeader MOD_HEADER(m_uniquemsg) = {
  30. "m_uniquemsg", // Module name
  31. "$Id: v1.02 2018/04/16 Gottem$", // Version
  32. "Implements chmode +U to prevent people from repeating messages", // Description
  33. "3.2-b8-1", // Modversion, not sure wat do
  34. NULL
  35. };
  36. // Initialisation routine (register hooks, commands and modes or create structs etc)
  37. MOD_INIT(m_uniquemsg) {
  38. uniquemsgMI = modinfo;
  39. // Request the mode flag
  40. CmodeInfo cmodereq;
  41. memset(&cmodereq, 0, sizeof(cmodereq));
  42. cmodereq.flag = CHMODE_FLAG; // Flag yo
  43. cmodereq.paracount = 0; // No params needed lol
  44. cmodereq.is_ok = uniquemsg_chmode_isok; // Custom verification function
  45. CmodeAdd(modinfo->handle, cmodereq, &uniquemsg_extcmode); // Now finally add the mode lol
  46. // Request moddata for storing the last message etc
  47. ModDataInfo mreq;
  48. memset(&mreq, 0, sizeof(mreq));
  49. mreq.type = MODDATATYPE_CHANNEL; // Apply to channels only
  50. mreq.name = "uniquemsg"; // Name it
  51. mreq.free = uniquemsg_md_free; // Function to free 'em
  52. umsgMDI = ModDataAdd(modinfo->handle, mreq);
  53. IsMDErr(umsgMDI, m_uniquemsg, modinfo);
  54. HookAddPChar(modinfo->handle, HOOKTYPE_PRE_CHANMSG, 0, uniquemsg_hook_prechanmsg);
  55. return MOD_SUCCESS; // Let MOD_LOAD handle errors
  56. }
  57. // Actually load the module here (also command overrides as they may not exist in MOD_INIT yet)
  58. MOD_LOAD(m_uniquemsg) {
  59. // Did the module throw an error during initialisation?
  60. if(ModuleGetError(uniquemsgMI->handle) != MODERR_NOERROR || !uniquemsg_extcmode) {
  61. // Display error string kek
  62. config_error("A critical error occurred when loading module %s: %s", MOD_HEADER(m_uniquemsg).name, ModuleGetErrorStr(uniquemsgMI->handle));
  63. return MOD_FAILED; // No good
  64. }
  65. return MOD_SUCCESS; // We good
  66. }
  67. // Called on unload/rehash obv
  68. MOD_UNLOAD(m_uniquemsg) {
  69. // Clean up any structs and other shit
  70. return MOD_SUCCESS; // We good
  71. }
  72. int is_chanowner_prot(aClient *sptr, aChannel *chptr) {
  73. Membership *lp; // For checkin' em list access level =]
  74. if(IsServer(sptr) || IsMe(sptr)) // Allow servers always lel
  75. return 1;
  76. if(chptr) { // Sanity cheqq
  77. if((lp = find_membership_link(sptr->user->channel, chptr))) {
  78. #ifdef PREFIX_AQ
  79. if(lp->flags & (CHFL_CHANOWNER|CHFL_CHANPROT))
  80. #else
  81. if(lp->flags & CHFL_CHANOP)
  82. #endif
  83. return 1;
  84. }
  85. }
  86. return 0; // No valid channel/membership or doesn't have enough axx lol
  87. }
  88. int wasRepeat(aChannel *chptr, char *text) {
  89. char *last, *clean;
  90. int repeat = 0;
  91. if((last = moddata_channel(chptr, umsgMDI).str)) {
  92. clean = (char *)StripControlCodes(text); // Strip all markup shit (bold, italikk etc) and colours
  93. if(clean && !stricmp(last, clean)) // Case-insensitive pls
  94. repeat = 1;
  95. }
  96. return repeat;
  97. }
  98. void uniquemsg_md_free(ModData *md) {
  99. if(md->str) // r u insaiyan?
  100. free(md->str);
  101. }
  102. int uniquemsg_chmode_isok(aClient *sptr, aChannel *chptr, char mode, char *para, int checkt, int what) {
  103. /* Args:
  104. ** sptr: Client who issues the MODE change
  105. ** chptr: Channel to which the MODE change applies
  106. ** mode: The mode character for completeness
  107. ** para: Parameter to the channel mode (will be NULL for paramless modes)
  108. ** checkt: Check type, one of EXCHK_*. Explained later.
  109. ** what: Used to differentiate between adding and removing the mode, one of MODE_ADD or MODE_DEL
  110. */
  111. /* Access types:
  112. ** EXCHK_ACCESS: Verify if the user may (un)set the mode, do NOT send error messages for this (just check access)
  113. ** EXCHK_ACCESS_ERR: Similar to above, but you SHOULD send an error message here
  114. ** EXCHK_PARAM: Check the sanity of the parameter(s)
  115. */
  116. /* Return values:
  117. ** EX_ALLOW: Allow it
  118. ** EX_DENY: Deny for most people (only IRC opers w/ override may use it)
  119. ** EX_ALWAYS_DENY: Even prevent IRC opers from overriding shit
  120. */
  121. if((checkt == EXCHK_ACCESS) || (checkt == EXCHK_ACCESS_ERR)) { // Access check lol
  122. // Check if the user has +a or +q (OperOverride automajikally overrides this bit ;])
  123. if(!is_chanowner_prot(sptr, chptr)) {
  124. if(checkt == EXCHK_ACCESS_ERR)
  125. sendto_one(sptr, err_str(ERR_CHANOWNPRIVNEEDED), me.name, sptr->name, chptr->chname);
  126. return EX_DENY;
  127. }
  128. return EX_ALLOW;
  129. }
  130. return EX_ALLOW; // Falltrough, like when someone attempts +U 10 it'll simply do +U
  131. }
  132. char *uniquemsg_hook_prechanmsg(aClient *sptr, aChannel *chptr, char *text, int notice) {
  133. char *clean;
  134. if(IsUMsg(chptr)) { // Only do shit when the chmode is set =]
  135. if(!is_chanownprotop(sptr, chptr) && wasRepeat(chptr, text)) { // People with +o and higher are exempt from the limitation
  136. sendto_one(sptr, ":%s NOTICE %s :This channel doesn't allow repeated messages", me.name, chptr->chname);
  137. return NULL; // Then fuck off =]
  138. }
  139. if(moddata_channel(chptr, umsgMDI).str) // Already got a string
  140. free(moddata_channel(chptr, umsgMDI).str); // Free it first
  141. clean = (char *)StripControlCodes(text);
  142. moddata_channel(chptr, umsgMDI).str = (clean ? strdup(clean) : NULL); // Then dup 'em
  143. }
  144. return text;
  145. }