├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ ├── config.yml │ └── feature_request.yml ├── pull_request_template.md └── workflows │ └── core-build.yml ├── .gitignore ├── LICENSE ├── README.md ├── conf ├── conf.sh.dist └── globalchat.conf.dist ├── data └── sql │ ├── db-characters │ └── base │ │ ├── globalchat_blacklist.sql │ │ └── globalchat_status.sql │ └── db-world │ └── base │ ├── globalchat_command.sql │ └── globalchat_strings.sql ├── icon.png ├── images ├── AllianceChat.jpg ├── ChatInfo.jpg ├── ChatPrefix.jpg ├── ClickChat.gif ├── GMChat.jpg ├── HordeChat.png ├── JoinChannel.gif ├── JoinLeaveGlobal.gif ├── PlayerIcons.jpg ├── PlayerName.jpg ├── Profanities.jpg └── Url.jpg ├── include.sh └── src ├── GlobalChat.cpp ├── GlobalChatData.cpp ├── GlobalChatData.h ├── GlobalChatMgr.cpp ├── GlobalChatMgr.h ├── GlobalChatScripts.cpp ├── GlobalChatScripts.h └── cs_globalchat.cpp /.gitattributes: -------------------------------------------------------------------------------- 1 | ## AUTO-DETECT 2 | ## Handle line endings automatically for files detected as 3 | ## text and leave all files detected as binary untouched. 4 | ## This will handle all files NOT defined below. 5 | * text=auto eol=lf 6 | 7 | # Text 8 | *.conf text 9 | *.conf.dist text 10 | *.cmake text 11 | 12 | ## Scripts 13 | *.sh text 14 | *.fish text 15 | *.lua text 16 | 17 | ## SQL 18 | *.sql text 19 | 20 | ## C++ 21 | *.c text 22 | *.cc text 23 | *.cxx text 24 | *.cpp text 25 | *.c++ text 26 | *.hpp text 27 | *.h text 28 | *.h++ text 29 | *.hh text 30 | 31 | 32 | ## For documentation 33 | 34 | # Documents 35 | *.doc diff=astextplain 36 | *.DOC diff=astextplain 37 | *.docx diff=astextplain 38 | *.DOCX diff=astextplain 39 | *.dot diff=astextplain 40 | *.DOT diff=astextplain 41 | *.pdf diff=astextplain 42 | *.PDF diff=astextplain 43 | *.rtf diff=astextplain 44 | *.RTF diff=astextplain 45 | 46 | ## DOCUMENTATION 47 | *.markdown text 48 | *.md text 49 | *.mdwn text 50 | *.mdown text 51 | *.mkd text 52 | *.mkdn text 53 | *.mdtxt text 54 | *.mdtext text 55 | *.txt text 56 | AUTHORS text 57 | CHANGELOG text 58 | CHANGES text 59 | CONTRIBUTING text 60 | COPYING text 61 | copyright text 62 | *COPYRIGHT* text 63 | INSTALL text 64 | license text 65 | LICENSE text 66 | NEWS text 67 | readme text 68 | *README* text 69 | TODO text 70 | 71 | ## GRAPHICS 72 | *.ai binary 73 | *.bmp binary 74 | *.eps binary 75 | *.gif binary 76 | *.ico binary 77 | *.jng binary 78 | *.jp2 binary 79 | *.jpg binary 80 | *.jpeg binary 81 | *.jpx binary 82 | *.jxr binary 83 | *.pdf binary 84 | *.png binary 85 | *.psb binary 86 | *.psd binary 87 | *.svg text 88 | *.svgz binary 89 | *.tif binary 90 | *.tiff binary 91 | *.wbmp binary 92 | *.webp binary 93 | 94 | 95 | ## ARCHIVES 96 | *.7z binary 97 | *.gz binary 98 | *.jar binary 99 | *.rar binary 100 | *.tar binary 101 | *.zip binary 102 | 103 | ## EXECUTABLES 104 | *.exe binary 105 | *.pyc binary 106 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug report 2 | description: Create a bug report to help us improve. 3 | title: '[Bug]: ' 4 | labels: 5 | - Bug 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | Thank you for taking the time to fill out a bug report. Remember to fill out all fields including the title above. 11 | An issue that is not properly filled out will be closed. 12 | - type: textarea 13 | id: current 14 | attributes: 15 | label: Current Behaviour 16 | description: | 17 | Description of the problem or issue here. 18 | If this is a crash, post the crashlog (upload to https://gist.github.com/) and include the link here. 19 | validations: 20 | required: true 21 | - type: textarea 22 | id: expected 23 | attributes: 24 | label: Expected Behaviour 25 | description: | 26 | What should happen instead. 27 | validations: 28 | required: true 29 | - type: textarea 30 | id: reproduce 31 | attributes: 32 | label: Steps to reproduce the problem 33 | description: | 34 | What does someone else need to do to encounter the same bug? 35 | placeholder: | 36 | 1. Step 1 37 | 2. Step 2 38 | 3. Step 3 39 | validations: 40 | required: true 41 | - type: textarea 42 | id: extra 43 | attributes: 44 | label: Extra Notes 45 | description: | 46 | Do you have any extra notes that can help solve the issue that does not fit any other field? 47 | placeholder: | 48 | None 49 | validations: 50 | required: false 51 | - type: textarea 52 | id: commit 53 | attributes: 54 | label: AC rev. hash/commit 55 | description: | 56 | Copy the result of the `.server debug` command (if you need to run it from the client get a prat addon) 57 | validations: 58 | required: true 59 | - type: input 60 | id: os 61 | attributes: 62 | label: Operating system 63 | description: | 64 | Operating System, i.e. Windows 10 x64, Debian 10 x64, etc 65 | validations: 66 | required: true 67 | - type: textarea 68 | id: custom 69 | attributes: 70 | label: Custom changes or Modules 71 | description: | 72 | List which custom changes or other modules you have applied, i.e. Eluna module, etc. 73 | placeholder: | 74 | None 75 | validations: 76 | required: false 77 | - type: markdown 78 | attributes: 79 | value: | 80 | Thank you for your contribution. 81 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: Feature request 2 | description: Suggest an idea for this project 3 | title: "[Feature]: " 4 | labels: 5 | - Suggestion 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | Thank you for taking your time to fill out a feature request. Remember to fill out all fields including the title above. 11 | An issue that is not properly filled out will be closed. 12 | - type: textarea 13 | id: description 14 | attributes: 15 | label: Describe your feature request or suggestion in detail 16 | description: | 17 | A clear and concise description of what you want to happen. 18 | validations: 19 | required: true 20 | - type: textarea 21 | id: solution 22 | attributes: 23 | label: Describe a possible solution to your feature or suggestion in detail 24 | description: | 25 | A clear and concise description of any alternative solutions or features you've considered. 26 | validations: 27 | required: false 28 | - type: textarea 29 | id: additional 30 | attributes: 31 | label: Additional context 32 | description: | 33 | Add any other context or screenshots about the feature request here. 34 | validations: 35 | required: false 36 | - type: markdown 37 | attributes: 38 | value: | 39 | Thank you for your contribution. 40 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Changes Proposed: 2 | 3 | 4 | - 5 | 6 | ## Issues Addressed: 7 | 8 | 9 | - Closes 10 | 11 | ## Known Issues and TODO List: 12 | 13 | 14 | - [ ] 15 | - [ ] 16 | -------------------------------------------------------------------------------- /.github/workflows/core-build.yml: -------------------------------------------------------------------------------- 1 | name: core-build 2 | on: 3 | push: 4 | branches: 5 | - 'master' 6 | pull_request: 7 | workflow_dispatch: 8 | 9 | jobs: 10 | build: 11 | uses: azerothcore/reusable-workflows/.github/workflows/core_build_modules.yml@main 12 | with: 13 | module_repo: ${{ github.event.repository.name }} 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | !.gitignore 2 | 3 | # 4 | #Generic 5 | # 6 | 7 | .directory 8 | .mailmap 9 | *.orig 10 | *.rej 11 | *.*~ 12 | .hg/ 13 | *.kdev* 14 | .DS_Store 15 | CMakeLists.txt.user 16 | *.bak 17 | *.patch 18 | *.diff 19 | *.REMOTE.* 20 | *.BACKUP.* 21 | *.BASE.* 22 | *.LOCAL.* 23 | 24 | # 25 | # IDE & other softwares 26 | # 27 | /.settings/ 28 | /.externalToolBuilders/* 29 | # exclude in all levels 30 | nbproject/ 31 | .sync.ffs_db 32 | *.kate-swp 33 | .idea 34 | 35 | # 36 | # Eclipse 37 | # 38 | *.pydevproject 39 | .metadata 40 | .gradle 41 | tmp/ 42 | *.tmp 43 | *.swp 44 | *~.nib 45 | local.properties 46 | .settings/ 47 | .loadpath 48 | .project 49 | .cproject 50 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU AFFERO GENERAL PUBLIC LICENSE 2 | Version 3, 19 November 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU Affero General Public License is a free, copyleft license for 11 | software and other kinds of works, specifically designed to ensure 12 | cooperation with the community in the case of network server software. 13 | 14 | The licenses for most software and other practical works are designed 15 | to take away your freedom to share and change the works. By contrast, 16 | our General Public Licenses are intended to guarantee your freedom to 17 | share and change all versions of a program--to make sure it remains free 18 | software for all its users. 19 | 20 | When we speak of free software, we are referring to freedom, not 21 | price. Our General Public Licenses are designed to make sure that you 22 | have the freedom to distribute copies of free software (and charge for 23 | them if you wish), that you receive source code or can get it if you 24 | want it, that you can change the software or use pieces of it in new 25 | free programs, and that you know you can do these things. 26 | 27 | Developers that use our General Public Licenses protect your rights 28 | with two steps: (1) assert copyright on the software, and (2) offer 29 | you this License which gives you legal permission to copy, distribute 30 | and/or modify the software. 31 | 32 | A secondary benefit of defending all users' freedom is that 33 | improvements made in alternate versions of the program, if they 34 | receive widespread use, become available for other developers to 35 | incorporate. Many developers of free software are heartened and 36 | encouraged by the resulting cooperation. However, in the case of 37 | software used on network servers, this result may fail to come about. 38 | The GNU General Public License permits making a modified version and 39 | letting the public access it on a server without ever releasing its 40 | source code to the public. 41 | 42 | The GNU Affero General Public License is designed specifically to 43 | ensure that, in such cases, the modified source code becomes available 44 | to the community. It requires the operator of a network server to 45 | provide the source code of the modified version running there to the 46 | users of that server. Therefore, public use of a modified version, on 47 | a publicly accessible server, gives the public access to the source 48 | code of the modified version. 49 | 50 | An older license, called the Affero General Public License and 51 | published by Affero, was designed to accomplish similar goals. This is 52 | a different license, not a version of the Affero GPL, but Affero has 53 | released a new version of the Affero GPL which permits relicensing under 54 | this license. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | TERMS AND CONDITIONS 60 | 61 | 0. Definitions. 62 | 63 | "This License" refers to version 3 of the GNU Affero General Public License. 64 | 65 | "Copyright" also means copyright-like laws that apply to other kinds of 66 | works, such as semiconductor masks. 67 | 68 | "The Program" refers to any copyrightable work licensed under this 69 | License. Each licensee is addressed as "you". "Licensees" and 70 | "recipients" may be individuals or organizations. 71 | 72 | To "modify" a work means to copy from or adapt all or part of the work 73 | in a fashion requiring copyright permission, other than the making of an 74 | exact copy. The resulting work is called a "modified version" of the 75 | earlier work or a work "based on" the earlier work. 76 | 77 | A "covered work" means either the unmodified Program or a work based 78 | on the Program. 79 | 80 | To "propagate" a work means to do anything with it that, without 81 | permission, would make you directly or secondarily liable for 82 | infringement under applicable copyright law, except executing it on a 83 | computer or modifying a private copy. Propagation includes copying, 84 | distribution (with or without modification), making available to the 85 | public, and in some countries other activities as well. 86 | 87 | To "convey" a work means any kind of propagation that enables other 88 | parties to make or receive copies. Mere interaction with a user through 89 | a computer network, with no transfer of a copy, is not conveying. 90 | 91 | An interactive user interface displays "Appropriate Legal Notices" 92 | to the extent that it includes a convenient and prominently visible 93 | feature that (1) displays an appropriate copyright notice, and (2) 94 | tells the user that there is no warranty for the work (except to the 95 | extent that warranties are provided), that licensees may convey the 96 | work under this License, and how to view a copy of this License. If 97 | the interface presents a list of user commands or options, such as a 98 | menu, a prominent item in the list meets this criterion. 99 | 100 | 1. Source Code. 101 | 102 | The "source code" for a work means the preferred form of the work 103 | for making modifications to it. "Object code" means any non-source 104 | form of a work. 105 | 106 | A "Standard Interface" means an interface that either is an official 107 | standard defined by a recognized standards body, or, in the case of 108 | interfaces specified for a particular programming language, one that 109 | is widely used among developers working in that language. 110 | 111 | The "System Libraries" of an executable work include anything, other 112 | than the work as a whole, that (a) is included in the normal form of 113 | packaging a Major Component, but which is not part of that Major 114 | Component, and (b) serves only to enable use of the work with that 115 | Major Component, or to implement a Standard Interface for which an 116 | implementation is available to the public in source code form. A 117 | "Major Component", in this context, means a major essential component 118 | (kernel, window system, and so on) of the specific operating system 119 | (if any) on which the executable work runs, or a compiler used to 120 | produce the work, or an object code interpreter used to run it. 121 | 122 | The "Corresponding Source" for a work in object code form means all 123 | the source code needed to generate, install, and (for an executable 124 | work) run the object code and to modify the work, including scripts to 125 | control those activities. However, it does not include the work's 126 | System Libraries, or general-purpose tools or generally available free 127 | programs which are used unmodified in performing those activities but 128 | which are not part of the work. For example, Corresponding Source 129 | includes interface definition files associated with source files for 130 | the work, and the source code for shared libraries and dynamically 131 | linked subprograms that the work is specifically designed to require, 132 | such as by intimate data communication or control flow between those 133 | subprograms and other parts of the work. 134 | 135 | The Corresponding Source need not include anything that users 136 | can regenerate automatically from other parts of the Corresponding 137 | Source. 138 | 139 | The Corresponding Source for a work in source code form is that 140 | same work. 141 | 142 | 2. Basic Permissions. 143 | 144 | All rights granted under this License are granted for the term of 145 | copyright on the Program, and are irrevocable provided the stated 146 | conditions are met. This License explicitly affirms your unlimited 147 | permission to run the unmodified Program. The output from running a 148 | covered work is covered by this License only if the output, given its 149 | content, constitutes a covered work. This License acknowledges your 150 | rights of fair use or other equivalent, as provided by copyright law. 151 | 152 | You may make, run and propagate covered works that you do not 153 | convey, without conditions so long as your license otherwise remains 154 | in force. You may convey covered works to others for the sole purpose 155 | of having them make modifications exclusively for you, or provide you 156 | with facilities for running those works, provided that you comply with 157 | the terms of this License in conveying all material for which you do 158 | not control copyright. Those thus making or running the covered works 159 | for you must do so exclusively on your behalf, under your direction 160 | and control, on terms that prohibit them from making any copies of 161 | your copyrighted material outside their relationship with you. 162 | 163 | Conveying under any other circumstances is permitted solely under 164 | the conditions stated below. Sublicensing is not allowed; section 10 165 | makes it unnecessary. 166 | 167 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 168 | 169 | No covered work shall be deemed part of an effective technological 170 | measure under any applicable law fulfilling obligations under article 171 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 172 | similar laws prohibiting or restricting circumvention of such 173 | measures. 174 | 175 | When you convey a covered work, you waive any legal power to forbid 176 | circumvention of technological measures to the extent such circumvention 177 | is effected by exercising rights under this License with respect to 178 | the covered work, and you disclaim any intention to limit operation or 179 | modification of the work as a means of enforcing, against the work's 180 | users, your or third parties' legal rights to forbid circumvention of 181 | technological measures. 182 | 183 | 4. Conveying Verbatim Copies. 184 | 185 | You may convey verbatim copies of the Program's source code as you 186 | receive it, in any medium, provided that you conspicuously and 187 | appropriately publish on each copy an appropriate copyright notice; 188 | keep intact all notices stating that this License and any 189 | non-permissive terms added in accord with section 7 apply to the code; 190 | keep intact all notices of the absence of any warranty; and give all 191 | recipients a copy of this License along with the Program. 192 | 193 | You may charge any price or no price for each copy that you convey, 194 | and you may offer support or warranty protection for a fee. 195 | 196 | 5. Conveying Modified Source Versions. 197 | 198 | You may convey a work based on the Program, or the modifications to 199 | produce it from the Program, in the form of source code under the 200 | terms of section 4, provided that you also meet all of these conditions: 201 | 202 | a) The work must carry prominent notices stating that you modified 203 | it, and giving a relevant date. 204 | 205 | b) The work must carry prominent notices stating that it is 206 | released under this License and any conditions added under section 207 | 7. This requirement modifies the requirement in section 4 to 208 | "keep intact all notices". 209 | 210 | c) You must license the entire work, as a whole, under this 211 | License to anyone who comes into possession of a copy. This 212 | License will therefore apply, along with any applicable section 7 213 | additional terms, to the whole of the work, and all its parts, 214 | regardless of how they are packaged. This License gives no 215 | permission to license the work in any other way, but it does not 216 | invalidate such permission if you have separately received it. 217 | 218 | d) If the work has interactive user interfaces, each must display 219 | Appropriate Legal Notices; however, if the Program has interactive 220 | interfaces that do not display Appropriate Legal Notices, your 221 | work need not make them do so. 222 | 223 | A compilation of a covered work with other separate and independent 224 | works, which are not by their nature extensions of the covered work, 225 | and which are not combined with it such as to form a larger program, 226 | in or on a volume of a storage or distribution medium, is called an 227 | "aggregate" if the compilation and its resulting copyright are not 228 | used to limit the access or legal rights of the compilation's users 229 | beyond what the individual works permit. Inclusion of a covered work 230 | in an aggregate does not cause this License to apply to the other 231 | parts of the aggregate. 232 | 233 | 6. Conveying Non-Source Forms. 234 | 235 | You may convey a covered work in object code form under the terms 236 | of sections 4 and 5, provided that you also convey the 237 | machine-readable Corresponding Source under the terms of this License, 238 | in one of these ways: 239 | 240 | a) Convey the object code in, or embodied in, a physical product 241 | (including a physical distribution medium), accompanied by the 242 | Corresponding Source fixed on a durable physical medium 243 | customarily used for software interchange. 244 | 245 | b) Convey the object code in, or embodied in, a physical product 246 | (including a physical distribution medium), accompanied by a 247 | written offer, valid for at least three years and valid for as 248 | long as you offer spare parts or customer support for that product 249 | model, to give anyone who possesses the object code either (1) a 250 | copy of the Corresponding Source for all the software in the 251 | product that is covered by this License, on a durable physical 252 | medium customarily used for software interchange, for a price no 253 | more than your reasonable cost of physically performing this 254 | conveying of source, or (2) access to copy the 255 | Corresponding Source from a network server at no charge. 256 | 257 | c) Convey individual copies of the object code with a copy of the 258 | written offer to provide the Corresponding Source. This 259 | alternative is allowed only occasionally and noncommercially, and 260 | only if you received the object code with such an offer, in accord 261 | with subsection 6b. 262 | 263 | d) Convey the object code by offering access from a designated 264 | place (gratis or for a charge), and offer equivalent access to the 265 | Corresponding Source in the same way through the same place at no 266 | further charge. You need not require recipients to copy the 267 | Corresponding Source along with the object code. If the place to 268 | copy the object code is a network server, the Corresponding Source 269 | may be on a different server (operated by you or a third party) 270 | that supports equivalent copying facilities, provided you maintain 271 | clear directions next to the object code saying where to find the 272 | Corresponding Source. Regardless of what server hosts the 273 | Corresponding Source, you remain obligated to ensure that it is 274 | available for as long as needed to satisfy these requirements. 275 | 276 | e) Convey the object code using peer-to-peer transmission, provided 277 | you inform other peers where the object code and Corresponding 278 | Source of the work are being offered to the general public at no 279 | charge under subsection 6d. 280 | 281 | A separable portion of the object code, whose source code is excluded 282 | from the Corresponding Source as a System Library, need not be 283 | included in conveying the object code work. 284 | 285 | A "User Product" is either (1) a "consumer product", which means any 286 | tangible personal property which is normally used for personal, family, 287 | or household purposes, or (2) anything designed or sold for incorporation 288 | into a dwelling. In determining whether a product is a consumer product, 289 | doubtful cases shall be resolved in favor of coverage. For a particular 290 | product received by a particular user, "normally used" refers to a 291 | typical or common use of that class of product, regardless of the status 292 | of the particular user or of the way in which the particular user 293 | actually uses, or expects or is expected to use, the product. A product 294 | is a consumer product regardless of whether the product has substantial 295 | commercial, industrial or non-consumer uses, unless such uses represent 296 | the only significant mode of use of the product. 297 | 298 | "Installation Information" for a User Product means any methods, 299 | procedures, authorization keys, or other information required to install 300 | and execute modified versions of a covered work in that User Product from 301 | a modified version of its Corresponding Source. The information must 302 | suffice to ensure that the continued functioning of the modified object 303 | code is in no case prevented or interfered with solely because 304 | modification has been made. 305 | 306 | If you convey an object code work under this section in, or with, or 307 | specifically for use in, a User Product, and the conveying occurs as 308 | part of a transaction in which the right of possession and use of the 309 | User Product is transferred to the recipient in perpetuity or for a 310 | fixed term (regardless of how the transaction is characterized), the 311 | Corresponding Source conveyed under this section must be accompanied 312 | by the Installation Information. But this requirement does not apply 313 | if neither you nor any third party retains the ability to install 314 | modified object code on the User Product (for example, the work has 315 | been installed in ROM). 316 | 317 | The requirement to provide Installation Information does not include a 318 | requirement to continue to provide support service, warranty, or updates 319 | for a work that has been modified or installed by the recipient, or for 320 | the User Product in which it has been modified or installed. Access to a 321 | network may be denied when the modification itself materially and 322 | adversely affects the operation of the network or violates the rules and 323 | protocols for communication across the network. 324 | 325 | Corresponding Source conveyed, and Installation Information provided, 326 | in accord with this section must be in a format that is publicly 327 | documented (and with an implementation available to the public in 328 | source code form), and must require no special password or key for 329 | unpacking, reading or copying. 330 | 331 | 7. Additional Terms. 332 | 333 | "Additional permissions" are terms that supplement the terms of this 334 | License by making exceptions from one or more of its conditions. 335 | Additional permissions that are applicable to the entire Program shall 336 | be treated as though they were included in this License, to the extent 337 | that they are valid under applicable law. If additional permissions 338 | apply only to part of the Program, that part may be used separately 339 | under those permissions, but the entire Program remains governed by 340 | this License without regard to the additional permissions. 341 | 342 | When you convey a copy of a covered work, you may at your option 343 | remove any additional permissions from that copy, or from any part of 344 | it. (Additional permissions may be written to require their own 345 | removal in certain cases when you modify the work.) You may place 346 | additional permissions on material, added by you to a covered work, 347 | for which you have or can give appropriate copyright permission. 348 | 349 | Notwithstanding any other provision of this License, for material you 350 | add to a covered work, you may (if authorized by the copyright holders of 351 | that material) supplement the terms of this License with terms: 352 | 353 | a) Disclaiming warranty or limiting liability differently from the 354 | terms of sections 15 and 16 of this License; or 355 | 356 | b) Requiring preservation of specified reasonable legal notices or 357 | author attributions in that material or in the Appropriate Legal 358 | Notices displayed by works containing it; or 359 | 360 | c) Prohibiting misrepresentation of the origin of that material, or 361 | requiring that modified versions of such material be marked in 362 | reasonable ways as different from the original version; or 363 | 364 | d) Limiting the use for publicity purposes of names of licensors or 365 | authors of the material; or 366 | 367 | e) Declining to grant rights under trademark law for use of some 368 | trade names, trademarks, or service marks; or 369 | 370 | f) Requiring indemnification of licensors and authors of that 371 | material by anyone who conveys the material (or modified versions of 372 | it) with contractual assumptions of liability to the recipient, for 373 | any liability that these contractual assumptions directly impose on 374 | those licensors and authors. 375 | 376 | All other non-permissive additional terms are considered "further 377 | restrictions" within the meaning of section 10. If the Program as you 378 | received it, or any part of it, contains a notice stating that it is 379 | governed by this License along with a term that is a further 380 | restriction, you may remove that term. If a license document contains 381 | a further restriction but permits relicensing or conveying under this 382 | License, you may add to a covered work material governed by the terms 383 | of that license document, provided that the further restriction does 384 | not survive such relicensing or conveying. 385 | 386 | If you add terms to a covered work in accord with this section, you 387 | must place, in the relevant source files, a statement of the 388 | additional terms that apply to those files, or a notice indicating 389 | where to find the applicable terms. 390 | 391 | Additional terms, permissive or non-permissive, may be stated in the 392 | form of a separately written license, or stated as exceptions; 393 | the above requirements apply either way. 394 | 395 | 8. Termination. 396 | 397 | You may not propagate or modify a covered work except as expressly 398 | provided under this License. Any attempt otherwise to propagate or 399 | modify it is void, and will automatically terminate your rights under 400 | this License (including any patent licenses granted under the third 401 | paragraph of section 11). 402 | 403 | However, if you cease all violation of this License, then your 404 | license from a particular copyright holder is reinstated (a) 405 | provisionally, unless and until the copyright holder explicitly and 406 | finally terminates your license, and (b) permanently, if the copyright 407 | holder fails to notify you of the violation by some reasonable means 408 | prior to 60 days after the cessation. 409 | 410 | Moreover, your license from a particular copyright holder is 411 | reinstated permanently if the copyright holder notifies you of the 412 | violation by some reasonable means, this is the first time you have 413 | received notice of violation of this License (for any work) from that 414 | copyright holder, and you cure the violation prior to 30 days after 415 | your receipt of the notice. 416 | 417 | Termination of your rights under this section does not terminate the 418 | licenses of parties who have received copies or rights from you under 419 | this License. If your rights have been terminated and not permanently 420 | reinstated, you do not qualify to receive new licenses for the same 421 | material under section 10. 422 | 423 | 9. Acceptance Not Required for Having Copies. 424 | 425 | You are not required to accept this License in order to receive or 426 | run a copy of the Program. Ancillary propagation of a covered work 427 | occurring solely as a consequence of using peer-to-peer transmission 428 | to receive a copy likewise does not require acceptance. However, 429 | nothing other than this License grants you permission to propagate or 430 | modify any covered work. These actions infringe copyright if you do 431 | not accept this License. Therefore, by modifying or propagating a 432 | covered work, you indicate your acceptance of this License to do so. 433 | 434 | 10. Automatic Licensing of Downstream Recipients. 435 | 436 | Each time you convey a covered work, the recipient automatically 437 | receives a license from the original licensors, to run, modify and 438 | propagate that work, subject to this License. You are not responsible 439 | for enforcing compliance by third parties with this License. 440 | 441 | An "entity transaction" is a transaction transferring control of an 442 | organization, or substantially all assets of one, or subdividing an 443 | organization, or merging organizations. If propagation of a covered 444 | work results from an entity transaction, each party to that 445 | transaction who receives a copy of the work also receives whatever 446 | licenses to the work the party's predecessor in interest had or could 447 | give under the previous paragraph, plus a right to possession of the 448 | Corresponding Source of the work from the predecessor in interest, if 449 | the predecessor has it or can get it with reasonable efforts. 450 | 451 | You may not impose any further restrictions on the exercise of the 452 | rights granted or affirmed under this License. For example, you may 453 | not impose a license fee, royalty, or other charge for exercise of 454 | rights granted under this License, and you may not initiate litigation 455 | (including a cross-claim or counterclaim in a lawsuit) alleging that 456 | any patent claim is infringed by making, using, selling, offering for 457 | sale, or importing the Program or any portion of it. 458 | 459 | 11. Patents. 460 | 461 | A "contributor" is a copyright holder who authorizes use under this 462 | License of the Program or a work on which the Program is based. The 463 | work thus licensed is called the contributor's "contributor version". 464 | 465 | A contributor's "essential patent claims" are all patent claims 466 | owned or controlled by the contributor, whether already acquired or 467 | hereafter acquired, that would be infringed by some manner, permitted 468 | by this License, of making, using, or selling its contributor version, 469 | but do not include claims that would be infringed only as a 470 | consequence of further modification of the contributor version. For 471 | purposes of this definition, "control" includes the right to grant 472 | patent sublicenses in a manner consistent with the requirements of 473 | this License. 474 | 475 | Each contributor grants you a non-exclusive, worldwide, royalty-free 476 | patent license under the contributor's essential patent claims, to 477 | make, use, sell, offer for sale, import and otherwise run, modify and 478 | propagate the contents of its contributor version. 479 | 480 | In the following three paragraphs, a "patent license" is any express 481 | agreement or commitment, however denominated, not to enforce a patent 482 | (such as an express permission to practice a patent or covenant not to 483 | sue for patent infringement). To "grant" such a patent license to a 484 | party means to make such an agreement or commitment not to enforce a 485 | patent against the party. 486 | 487 | If you convey a covered work, knowingly relying on a patent license, 488 | and the Corresponding Source of the work is not available for anyone 489 | to copy, free of charge and under the terms of this License, through a 490 | publicly available network server or other readily accessible means, 491 | then you must either (1) cause the Corresponding Source to be so 492 | available, or (2) arrange to deprive yourself of the benefit of the 493 | patent license for this particular work, or (3) arrange, in a manner 494 | consistent with the requirements of this License, to extend the patent 495 | license to downstream recipients. "Knowingly relying" means you have 496 | actual knowledge that, but for the patent license, your conveying the 497 | covered work in a country, or your recipient's use of the covered work 498 | in a country, would infringe one or more identifiable patents in that 499 | country that you have reason to believe are valid. 500 | 501 | If, pursuant to or in connection with a single transaction or 502 | arrangement, you convey, or propagate by procuring conveyance of, a 503 | covered work, and grant a patent license to some of the parties 504 | receiving the covered work authorizing them to use, propagate, modify 505 | or convey a specific copy of the covered work, then the patent license 506 | you grant is automatically extended to all recipients of the covered 507 | work and works based on it. 508 | 509 | A patent license is "discriminatory" if it does not include within 510 | the scope of its coverage, prohibits the exercise of, or is 511 | conditioned on the non-exercise of one or more of the rights that are 512 | specifically granted under this License. You may not convey a covered 513 | work if you are a party to an arrangement with a third party that is 514 | in the business of distributing software, under which you make payment 515 | to the third party based on the extent of your activity of conveying 516 | the work, and under which the third party grants, to any of the 517 | parties who would receive the covered work from you, a discriminatory 518 | patent license (a) in connection with copies of the covered work 519 | conveyed by you (or copies made from those copies), or (b) primarily 520 | for and in connection with specific products or compilations that 521 | contain the covered work, unless you entered into that arrangement, 522 | or that patent license was granted, prior to 28 March 2007. 523 | 524 | Nothing in this License shall be construed as excluding or limiting 525 | any implied license or other defenses to infringement that may 526 | otherwise be available to you under applicable patent law. 527 | 528 | 12. No Surrender of Others' Freedom. 529 | 530 | If conditions are imposed on you (whether by court order, agreement or 531 | otherwise) that contradict the conditions of this License, they do not 532 | excuse you from the conditions of this License. If you cannot convey a 533 | covered work so as to satisfy simultaneously your obligations under this 534 | License and any other pertinent obligations, then as a consequence you may 535 | not convey it at all. For example, if you agree to terms that obligate you 536 | to collect a royalty for further conveying from those to whom you convey 537 | the Program, the only way you could satisfy both those terms and this 538 | License would be to refrain entirely from conveying the Program. 539 | 540 | 13. Remote Network Interaction; Use with the GNU General Public License. 541 | 542 | Notwithstanding any other provision of this License, if you modify the 543 | Program, your modified version must prominently offer all users 544 | interacting with it remotely through a computer network (if your version 545 | supports such interaction) an opportunity to receive the Corresponding 546 | Source of your version by providing access to the Corresponding Source 547 | from a network server at no charge, through some standard or customary 548 | means of facilitating copying of software. This Corresponding Source 549 | shall include the Corresponding Source for any work covered by version 3 550 | of the GNU General Public License that is incorporated pursuant to the 551 | following paragraph. 552 | 553 | Notwithstanding any other provision of this License, you have 554 | permission to link or combine any covered work with a work licensed 555 | under version 3 of the GNU General Public License into a single 556 | combined work, and to convey the resulting work. The terms of this 557 | License will continue to apply to the part which is the covered work, 558 | but the work with which it is combined will remain governed by version 559 | 3 of the GNU General Public License. 560 | 561 | 14. Revised Versions of this License. 562 | 563 | The Free Software Foundation may publish revised and/or new versions of 564 | the GNU Affero General Public License from time to time. Such new versions 565 | will be similar in spirit to the present version, but may differ in detail to 566 | address new problems or concerns. 567 | 568 | Each version is given a distinguishing version number. If the 569 | Program specifies that a certain numbered version of the GNU Affero General 570 | Public License "or any later version" applies to it, you have the 571 | option of following the terms and conditions either of that numbered 572 | version or of any later version published by the Free Software 573 | Foundation. If the Program does not specify a version number of the 574 | GNU Affero General Public License, you may choose any version ever published 575 | by the Free Software Foundation. 576 | 577 | If the Program specifies that a proxy can decide which future 578 | versions of the GNU Affero General Public License can be used, that proxy's 579 | public statement of acceptance of a version permanently authorizes you 580 | to choose that version for the Program. 581 | 582 | Later license versions may give you additional or different 583 | permissions. However, no additional obligations are imposed on any 584 | author or copyright holder as a result of your choosing to follow a 585 | later version. 586 | 587 | 15. Disclaimer of Warranty. 588 | 589 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 590 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 591 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 592 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 593 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 594 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 595 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 596 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 597 | 598 | 16. Limitation of Liability. 599 | 600 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 601 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 602 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 603 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 604 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 605 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 606 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 607 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 608 | SUCH DAMAGES. 609 | 610 | 17. Interpretation of Sections 15 and 16. 611 | 612 | If the disclaimer of warranty and limitation of liability provided 613 | above cannot be given local legal effect according to their terms, 614 | reviewing courts shall apply local law that most closely approximates 615 | an absolute waiver of all civil liability in connection with the 616 | Program, unless a warranty or assumption of liability accompanies a 617 | copy of the Program in return for a fee. 618 | 619 | END OF TERMS AND CONDITIONS 620 | 621 | How to Apply These Terms to Your New Programs 622 | 623 | If you develop a new program, and you want it to be of the greatest 624 | possible use to the public, the best way to achieve this is to make it 625 | free software which everyone can redistribute and change under these terms. 626 | 627 | To do so, attach the following notices to the program. It is safest 628 | to attach them to the start of each source file to most effectively 629 | state the exclusion of warranty; and each file should have at least 630 | the "copyright" line and a pointer to where the full notice is found. 631 | 632 | 633 | Copyright (C) 634 | 635 | This program is free software: you can redistribute it and/or modify 636 | it under the terms of the GNU Affero General Public License as published 637 | by the Free Software Foundation, either version 3 of the License, or 638 | (at your option) any later version. 639 | 640 | This program is distributed in the hope that it will be useful, 641 | but WITHOUT ANY WARRANTY; without even the implied warranty of 642 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 643 | GNU Affero General Public License for more details. 644 | 645 | You should have received a copy of the GNU Affero General Public License 646 | along with this program. If not, see . 647 | 648 | Also add information on how to contact you by electronic and paper mail. 649 | 650 | If your software can interact with users remotely through a computer 651 | network, you should also make sure that it provides a way for users to 652 | get its source. For example, if your program is a web application, its 653 | interface could display a "Source" link that leads users to an archive 654 | of the code. There are many ways you could offer source, and different 655 | solutions will be better for different programs; see section 13 for the 656 | specific requirements. 657 | 658 | You should also get your employer (if you work as a programmer) or school, 659 | if any, to sign a "copyright disclaimer" for the program, if necessary. 660 | For more information on this, and how to apply and follow the GNU AGPL, see 661 | . -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # This is a module for ![logo](https://raw.githubusercontent.com/azerothcore/azerothcore.github.io/master/images/logo-github.png) AzerothCore 2 | 3 | ## Advanced GlobalChat 4 | 5 | [![core-build](https://github.com/Gozzim/mod-globalchat/actions/workflows/core-build.yml/badge.svg)](https://github.com/Gozzim/mod-globalchat) 6 | [![core-version](https://img.shields.io/badge/core--rev-58302e4-ff0000.svg?labelColor=2e2c2c&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABQCAYAAADvCdDvAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4AcZDi06qRnbcAAACGxJREFUeNrtnH+QVlUZxz/nlbAhwBoEyQAhycpGxxhUyLGhYtTK8IwEXhUwEQezNinAJEwbIwU1MN0QIURNnCuiHWUsw7Q0mqByY8DEsB9AoyLgD8IfwbL79Md93mFdd9973/c9991dON+ZMzuw97333OdzfjzPc553oQtLoCAwRmCDwCkEdTiQUQKibbPAScEqHQdjhMA7LYCIQIPACcE6tYcxTOCVVjCK7WmB44OVagfjRIHn24FRbKsFPhaslR8Eoz8/IbA2BUaxrRIYFKyXH5TBAo9lhFFsDwv0DdbzD+MogbhMGC2hfCBY0R+M3gKLKoRRbE6gEKxZPYzDBeZUCaPYHgoWrQ5GN4E6TzBEoFng/mDZymAcJnCeRxjF1iSwNFi4/PzUaIH9OQARve+CYOnsQE4R2JMTjGLbK3BdsHa2KPzlnGEU21sCM4P31T6MT2ZIifhubwh8Q+CwQODdMIYKrKsxjGLbKTA5zJQDMAYIPN5BMIrtFYHzAwzoI/BQB8MotpcFzjmUYfQQWKYBm3SStl1g9KEa+C3IMdaodk859VADMltgXyeEUWyvHjJHwQKXamDmy3j7BH6VQzC5S2DowQ7jHA3IfKZB7tF7j89hCXxRYPDBCuOzOup8GuyRFvc3Ovt8L4UvCAw82GB8WmCLZ0M90TqY08Tk9DbKg6ptDQLHHEwpkQ2eDbRG4IgSHtw1OUD5XZdfvgSGqPHEY7yxNq2aRKC7wFzPzoMIPNplZ4pA/wqqREqd9hWXjhPLCDzn5xB4rhQ4uqvB6J1DSmSTwOll9qOnQH0OccpygSO7Coxu2mGfBtgq8JUK+9NLYEkOUO4S6NkVgNwh0Og5lTGxyj71Erg3ByhLBLp1ZhhzPXs3ewSmeepbT4EHc4ByW2eFMcNz+uJ/AtenPTeytntk7c2RtfMyzpTHcoByoy87FjzBmATMxt+a2ggsN/C9FBgF4CpgOnB5ZO3MUtcb2ANcADzleTzOFLjGx42MBxhfIql16u/p5ZqAR03KYVFkrQEuAxa2+O//AlfFzt2e0uejSSoZfafZpxuY32EzROAzwC0eYQCsAcZmuO78VjAAegNzImsnpozCl0gchWc8A/mxwNc7ZIZogLYUGO7xhdYBZxrYnTI7xgC/KDGgdgB1sXMr0nJswJ34/W7iPuAyA8tqNkMEjiWp/hue/NOLGoBxGWCcAfw8pe/9gJ9E1tqU0fhXoA74m0cg3YFbBCbUBIgky9MC4PMKo5p9qAjzWWCSgf+kwBgFLNGlKU39gVsja7+cAmUNcAWw2SOU3sDNAuflCkQj01s5EDVX6xQY4AVgqkkZpZG1p+qzy/ma2kCgPrL2rJROPKFQtnmEchRwk4DNZQ/Rs4d7gAs9dvpF4BIDv06BcQKwiMSJqERbgSmxc79Jeccv6jv6zFNt0QG32jeQnwGTfbjKqtd181uRAmOIelNnVfm8bcCFsXNrMrjxDwA9PEOZYOAPXpYsLd+f4BHGOxrQPZACoy/wIw8w0KXuvsjaESkj9JfAOI/OCiQHW/dmOTooZIDxfWAKcLinzu0HfggsMSVeOrK2F8mzfZZ4DgTiyNphGaBY/GowyVe1h1QMRJJI+NseUyLNwE+BeSkw3q+bbF0OabdjFMqnUq5bBYz3/OxBwJOSbPjl7SEC5wL1wIc9dUaAWN3b/SVgvA+4GLiDfLUBGB879/cSHTbA1zR49KnNwEgDr2WaIQKfA+Z6hrEauDQFRgEYUwMYkKznyyJrjysxWkW9rmmen30cSdXMh1KBCAwjSSf7/FshfwQmGngrBcYZwEpqp5HAQvXk2oPSBNyu+1mzx2efpHvKke0CEfi4wvCZn1qvLt/OlOv61BhGUSPS3tck+an5wE2UmOEV6DT1vvq9B4jAAF2mvuDxgc8DFxv4d4ZrG6n998jfBn4AuAwB29vADTpbfEI5E1hY3OgLCuODOjN8unpbgCkmmSGpip17A7gcWFwjGPtIjg7qY+caM0bRuxXgXZ6Xr3M1zdLP6CH9YvVsfGmH7hmry/1gZO0RwDxgao4wmtVxuDJ27s0KPJS+CvMCz/26s6BpCZ8w3iwnd9PGTNkNXJ2zp7USmFUJDJ0pO9Xzch77tBtYX1B/3JeaSE7MHqnmJrFzu1osDb71W2Cqgq9YCuUSkkyxj1TSDUB9geTUb5anl50G3G88rK+xc9t1pqzwCGMjxozV/apqaWD3VZKTzkrVCNxmNHtR/NN5PYArgWuruPF1wPUG9voczpG1A9SzObvKW20DTo6d2+F7ymkl/u8p/2twzcDdJsmivzt1oodP1wIzKujTImBGqcCvSiiDgLuBURXe4lVgeOzclrw2JY2612oUnjV7sap1dY1pg/RckqRiVj2oKZHX8/RRI2uHAvcBJ5f50T3AqNi5hrz9aC0vepqk5iBNT5k2BlihDT/7as3fZNGTwBV5w9A95R/ARSSFCeUEfmNrAUPt9xLJqeO/Ui5dpwEhqbksk0zv72bYTP8MfNMkx7C5K7LWxM5tUigbM8KYEjv3eC1Df5PUCIwrAeUZ4Oz29tpCOzfdrntJe372cwpjk/g7RUybIaJQNpJsgs+luJGzPHto5UBp0D7+s9Wv1gPjDexq77Ptpd+NluR8h+T0rKW2AnUG/qTXSa1etAWUv2gM0Falyl6SJODi2LkmOkgmqR+u40Ae71lgsklZzkodUBmTVHZ/VKPm0eqtTDLvhdQhiqwdSVJ8cXyLwHQhMDt2bk9n6KMkp44XAfNNhiDSZLxpsVJxqYGH6USKrD1NXeJjgeXAt2LnXuss/ZOkkrEPsNNkyBKXUwb0kVpt4BVAOZ2kSnCORvhBQUFBQUFBQUFBQUFBQUFBQUFBQUFBHaj/A+W3qVU1bYv9AAAAAElFTkSuQmCC)](https://github.com/azerothcore/azerothcore-wotlk/commit/58302e41969e16dfe1ba93792622d990c03f2b2f) 7 | [![CodeFactor](https://www.codefactor.io/repository/github/gozzim/mod-globalchat/badge)](https://www.codefactor.io/repository/github/gozzim/mod-globalchat) 8 | 9 | ## Description 10 | 11 | **Advanced GlobalChat** with around **30 configuration options**.\ 12 | This GlobalChat allows you to configure the module to your individual needs! 13 | 14 | ## Requirements 15 | Requires at least revision [azerohcore-wotlk@58302e4](https://github.com/azerothcore/azerothcore-wotlk/commit/58302e41969e16dfe1ba93792622d990c03f2b2f) of AzerothCore 16 | 17 | ## Features 18 | 19 | - Enable/Disable Chat per command 20 | - Players can join and leave the chat 21 | - Enable/Disable Faction specific chats 22 | - Set Chat Name/NameColor/TextColor 23 | - Configure Class/Race/Faction Icons to your need 24 | - Spam Protection 25 | - URL Filters/Censoring 26 | - Offensive language Filters/Censoring 27 | - Support for custom GM ranks 28 | - Information of players having left or joined the Chat, their Mutes and Bans in the GlobalChat is saved and loaded. 29 | - And a total of almost **30 Configuration Options** to customize the Chat to your needs! 30 | 31 | ## Commands 32 | 33 | ### Player Commands 34 | 35 | - `.chat` or `.global` - Write a message in the GlobalChat 36 | - `.joinglobal` - Join the GlobalChat 37 | - `.leaveglobal` - Leave the GlobalChat 38 | - `/join ` - Join the GlobalChat like a normal Channel (Name configured in conf file) 39 | 40 | ### GM Commands 41 | 42 | - `.genable` - Enables the GlobalChat 43 | - `.gdisable` - Disables the GlobalChat (GMs can still send messages) 44 | - `.gmute` - Mute a player in the GlobalChat for a certain amount of time or permanently 45 | - `.gunmute` - Unmute a player in the GlobalChat 46 | - `.ginfo` - Get information about player (Mutes, etc.) 47 | - `.galliance` or `.ghorde` - Chat in Faction GlobalChat (if Faction specific chats are enabled) 48 | - `.gblacklist` - Add, Remove or Reload Phrases to and from the Blacklisted Obscenities in the GlobalChat 49 | 50 | ## Screenshots 51 | 52 | | Chat Customization | **Faction Specific Chat** | 53 | |:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:| 54 | | *Chat Name & Prefix* Url
*Player Icons* Icons
*Player Name* PlayerName | *Viewpoint of the Horde* Horde
*Viewpoint of the Alliance* Alliance
*Viewpoint of a GM* GM | 55 | | **Filtering** | **Improved Usability** | 56 | | *Offensive Language* Profanity
*URLs* Url
*PlayerInfo* PlayerInfo | *Join and Leave the Chat* Join
*Click to Chat* Click
*Use like a Channel* Channel | 57 | 58 | ## Installation 59 | 60 | ``` 61 | 1) Simply place the module under the `modules` directory of your AzerothCore source. 62 | 2) Import the SQL into the Database. 63 | 3) Re-run cmake and launch a clean build of AzerothCore. 64 | ``` 65 | 66 | ## Edit module configuration (optional) 67 | 68 | If you need to change the module configuration, go to your server configuration folder (where your worldserver or worldserver.exe is), copy globalchat.conf.dist to globalchat.conf and edit that new file. 69 | 70 | ## Credits 71 | 72 | - Module for AzerothCore created by [Gozzim](https://github.com/Gozzim) 73 | - AzerothCore: [repository](https://github.com/azerothcore) - [website](http://azerothcore.org/) 74 | 75 | ## License 76 | 77 | This code and content is released under the [GNU AGPL-3.0 license](https://github.com/Gozzim/mod-globalchat/blob/master/LICENSE). 78 | -------------------------------------------------------------------------------- /conf/conf.sh.dist: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ## CUSTOM SQL - Important file used by the db_assembler.sh 4 | ## Keep only the required variables (base sql files or updates, depending on the DB) 5 | 6 | 7 | ## BASE SQL 8 | 9 | DB_AUTH_CUSTOM_PATHS+=( 10 | $MOD_SPECTATOR_ROOT"/sql/auth/base/" 11 | ) 12 | 13 | DB_CHARACTERS_CUSTOM_PATHS+=( 14 | $MOD_SPECTATOR_ROOT"/sql/characters/base/" 15 | ) 16 | 17 | DB_WORLD_CUSTOM_PATHS+=( 18 | $MOD_SPECTATOR_ROOT"/sql/world/base/" 19 | ) 20 | 21 | 22 | ## UPDATES 23 | 24 | DB_AUTH_UPDATE_PATHS+=( 25 | $MOD_SPECTATOR_ROOT"/sql/auth/updates/" 26 | ) 27 | 28 | DB_CHARACTERS_UPDATE_PATHS+=( 29 | $MOD_SPECTATOR_ROOT"/sql/characters/updates/" 30 | ) 31 | 32 | DB_WORLD_UPDATE_PATHS+=( 33 | $MOD_SPECTATOR_ROOT"/sql/world/updates/" 34 | ) 35 | -------------------------------------------------------------------------------- /conf/globalchat.conf.dist: -------------------------------------------------------------------------------- 1 | [worldserver] 2 | 3 | ################################################################################################### 4 | # 5 | # GlobalChat.Enable 6 | # Description: Enable GlobalChat 7 | # Default: 1 - (Enabled) 8 | # 0 - (Disabled) 9 | 10 | GlobalChat.Enable = 1 11 | 12 | # GlobalChat.Announce 13 | # Description: Announce Module 14 | # Default: 1 - (Enabled) 15 | # 0 - (Disabled) 16 | 17 | GlobalChat.Announce = 1 18 | 19 | # 20 | # GlobalChat.Chat.Name 21 | # Description: GlobalChat Name 22 | # Default: "Global" 23 | # 24 | 25 | GlobalChat.Chat.Name = "Global" 26 | 27 | # 28 | # GlobalChat.Chat.NameColor 29 | # Description: Color the GlobalChat Name is displayed in 30 | # Default: "FFFF00" 31 | # 32 | 33 | GlobalChat.Chat.NameColor = "FFFF00" 34 | 35 | # 36 | # GlobalChat.Chat.TextColor 37 | # Description: Changes the color the GlobalChat content is displayed in 38 | # Default: "FFFFFF" 39 | # 40 | 41 | GlobalChat.Chat.TextColor = "FFFFFF" 42 | 43 | # 44 | # GlobalChat.Player.NameColor 45 | # Description: Color of the PlayerName 46 | # Default: 1 - (Class) 47 | # 2 - (Faction) 48 | # 0 - (Disabled) 49 | # 50 | 51 | GlobalChat.Player.NameColor = 1 52 | 53 | # 54 | # GlobalChat.Player.FactionIcon 55 | # Description: Enable showing a faction icon in front of the PlayerName 56 | # Default: 0 - (Disabled) 57 | # 1 - (Enabled) 58 | # 59 | 60 | GlobalChat.Player.FactionIcon = 0 61 | 62 | # 63 | # GlobalChat.Player.RaceIcon 64 | # Description: Enable showing a race icon in front of the PlayerName 65 | # Default: 1 - (Enabled) 66 | # 0 - (Disabled) 67 | # 68 | 69 | GlobalChat.Player.RaceIcon = 1 70 | 71 | # 72 | # GlobalChat.Player.ClassIcon 73 | # Description: Enable showing a class icon in front of the PlayerName 74 | # Default: 0 - (Disabled) 75 | # 1 - (Enabled) 76 | # 77 | 78 | GlobalChat.Player.ClassIcon = 0 79 | 80 | # 81 | # GlobalChat.GM.Colors 82 | # Description: Colors for names of GMs in GlobalChat. Ordered by Rank, starting at 1. 83 | # If a Rank should not have a GMColor, leave it empty. 84 | # Examples: "00FF00;091FE0;FF0000" 85 | # "0000FF;FF0000;000000" 86 | # "00FF00;;FF0000" 87 | # ";091FE0;FF0000" 88 | # 89 | # Default: "00FF00;091FE0;FF0000" 90 | # "" - (Disabled) 91 | # 92 | 93 | GlobalChat.GM.Colors = "00FF00;091FE0;FF0000" 94 | 95 | # 96 | # GlobalChat.GM.Badge 97 | # Description: Configure when to show the GMChatBadge and GMColors in GlobalChat 98 | # Default: 1 - (Always) 99 | # 2 - (With GM/Dev On) 100 | # 3 - (With Normal GMChatBadge On) 101 | # 0 - (Never) 102 | # 103 | 104 | GlobalChat.GM.Badge = 1 105 | 106 | # 107 | # GlobalChat.FactionSpecific 108 | # Description: Enable faction specific GlobalChat 109 | # Default: 0 - (Disabled) 110 | # 1 - (Enabled) 111 | # 112 | 113 | GlobalChat.FactionSpecific = 0 114 | 115 | # 116 | # GlobalChat.OnFirstLogin 117 | # Description: Enable GlobalChat by default on first character login 118 | # Default: 1 - (Enabled) 119 | # 0 - (Disabled) 120 | # 121 | 122 | GlobalChat.OnFirstLogin = 1 123 | 124 | # 125 | # GlobalChat.PlayTimeToChat 126 | # Description: Sets PlayTime until GlobalChat can be used 127 | # Default: 300 - (seconds) 128 | # 0 - (Disabled) 129 | # 130 | 131 | GlobalChat.PlayTimeToChat = 300 132 | 133 | # 134 | # GlobalChat.CoolDown 135 | # Description: Time until a new message can be sent in GlobalChat 136 | # Default: 2 - (seconds) 137 | # 0 - (Disabled) 138 | # 139 | 140 | GlobalChat.CoolDown = 2 141 | 142 | # 143 | # GlobalChat.SendToIgnored 144 | # Description: Enables sending messages to players even if the sender is ignored by the receiver 145 | # Default: 0 - (Disabled) 146 | # 1 - (Enabled) 147 | # 148 | 149 | GlobalChat.SendToIgnored = 0 150 | 151 | # 152 | # GlobalChat.JoinChannelAllowed 153 | # Description: Enables /join for GlobalChat 154 | # Default: 0 - (Disabled) 155 | # 1 - (Enabled) 156 | # 157 | 158 | GlobalChat.JoinChannelAllowed = 0 159 | 160 | # 161 | # GlobalChat.AnnounceMutes 162 | # Description: Enables announcements of GlobalChat mutes executed by GM Commands 163 | # Default: 0 - (Disabled) 164 | # 1 - (Enabled) 165 | # 166 | 167 | GlobalChat.AnnounceMutes = 0 168 | 169 | # 170 | # GlobalChat.Profanity.BlockType 171 | # Description: How to handle players posting Profanities. 172 | # Default: 1 - (Censor Profanity) 173 | # 2 - (Block Message) 174 | # 3 - (Block Message & Mute Player) 175 | # 0 - (Disabled) 176 | # 177 | 178 | GlobalChat.Profanity.BlockType = 1 179 | 180 | # 181 | # GlobalChat.Profanity.BlockLevel 182 | # Description: Player Security Level (Rank) to block profanities. Also supports custom levels. 183 | # Default: 0 - (Players) 184 | # 1 - (Players + Moderators) 185 | # 2 - (Players + Moderators + GMs) 186 | # 3 - (Players + Moderators + GMs + Admins) 187 | # 188 | 189 | GlobalChat.Profanity.BlockLevel = 0 190 | 191 | # 192 | # GlobalChat.Profanity.MuteType 193 | # Description: The scale on which players get muted when using profanities. 194 | # Default: 1 - (GlobalChat only) 195 | # 2 - (GlobalChat + Global) 196 | # 0 - (Disabled) 197 | # 198 | 199 | GlobalChat.Profanity.MuteType = 1 200 | 201 | # 202 | # GlobalChat.Profanity.MuteTime 203 | # Description: MuteTime when player uses profanities or banned phrases. 204 | # Default: 30 - (seconds) 205 | # 0 - (Disabled) 206 | # 207 | 208 | GlobalChat.Profanity.MuteTime = 30 209 | 210 | # 211 | # GlobalChat.Profanity.FromDBC 212 | # Description: Load profanities from DBC 213 | # Default: 0 - (Disabled) 214 | # 1 - (Enabled) 215 | # 216 | 217 | GlobalChat.Profanity.FromDBC = 0 218 | 219 | # 220 | # GlobalChat.URL.BlockType 221 | # Description: How to handle players posting URLs. 222 | # Default: 1 - (Censor URL) 223 | # 2 - (Block Message) 224 | # 3 - (Block Message & Mute Player) 225 | # 0 - (Disabled) 226 | # 227 | 228 | GlobalChat.URL.BlockType = 1 229 | 230 | # 231 | # GlobalChat.URL.BlockLevel 232 | # Description: Player Security Level (Rank) to block URLs. Also supports custom levels. 233 | # Default: 0 - (Players) 234 | # 1 - (Players + Moderators) 235 | # 2 - (Players + Moderators + GMs) 236 | # 3 - (Players + Moderators + GMs + Admins) 237 | # 238 | 239 | GlobalChat.URL.BlockLevel = 0 240 | 241 | # 242 | # GlobalChat.URL.MuteType 243 | # Description: The scale on which players get muted when using URLs. 244 | # Default: 1 - (GlobalChat only) 245 | # 2 - (GlobalChat + Global) 246 | # 0 - (Disabled) 247 | # 248 | 249 | GlobalChat.URL.MuteType = 1 250 | 251 | # 252 | # GlobalChat.URL.MuteTime 253 | # Description: MuteTime when player posts URL. GMs will not get muted. 254 | # Default: 120 - (seconds) 255 | # 0 - (Disabled) 256 | # 257 | 258 | GlobalChat.URL.MuteTime = 120 259 | 260 | # 261 | # GlobalChat.URL.Whitelist 262 | # Description: Whitelisted Domains separated by Semicolons (;) 263 | # Example: (azerothcore.org;wowhead.com) 264 | # Default: "" 265 | # 266 | 267 | GlobalChat.URL.Whitelist = "" 268 | 269 | # 270 | ################################################################################################### -------------------------------------------------------------------------------- /data/sql/db-characters/base/globalchat_blacklist.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS `globalchat_blacklist`; 2 | CREATE TABLE IF NOT EXISTS `globalchat_blacklist` ( 3 | `phrase` VARCHAR(255) NOT NULL, 4 | PRIMARY KEY (`phrase`) USING BTREE 5 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 6 | -------------------------------------------------------------------------------- /data/sql/db-characters/base/globalchat_status.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS `player_globalchat_status`; 2 | CREATE TABLE IF NOT EXISTS `player_globalchat_status` ( 3 | `guid` int unsigned NOT NULL DEFAULT 0, 4 | `enabled` BOOL NOT NULL DEFAULT '0', 5 | `last_msg` int unsigned NOT NULL DEFAULT 0, 6 | `mute_time` int unsigned NOT NULL DEFAULT 0, 7 | `total_mutes` bigint unsigned NOT NULL DEFAULT 0, 8 | `banned` BOOL NOT NULL DEFAULT '0', 9 | PRIMARY KEY (`guid`) 10 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 11 | -------------------------------------------------------------------------------- /data/sql/db-world/base/globalchat_command.sql: -------------------------------------------------------------------------------- 1 | DELETE FROM `command` WHERE `name` IN ('global','g','chat','c','joinglobal','leaveglobal','genable','gdisable','gmute','gunmute','ginfo','galliance','ghorde','gblacklist','gblacklist add','gblacklist remove','gblacklist reload'); 2 | INSERT INTO `command` (`name`, `security`, `help`) VALUES 3 | ('global', 0, 'Syntax: .global $text\nWrite a message in the GlobalChat.\nAdditional commands: .joinglobal & .leaveglobal'), 4 | ('g', 0, 'Syntax: .g $text\nWrite a message in the GlobalChat.\nAdditional commands: .joinglobal & .leaveglobal'), 5 | ('chat', 0, 'Syntax: .chat $text\nWrite a message in the GlobalChat.\nAdditional commands: .joinglobal & .leaveglobal'), 6 | ('c', 0, 'Syntax: .c $text\nWrite a message in the GlobalChat.\nAdditional commands: .joinglobal & .leaveglobal'), 7 | ('joinglobal', 0, 'Join the GlobalChat.'), 8 | ('leaveglobal', 0, 'Leave the GlobalChat.'), 9 | ('ginfo', 1, 'Syntax: $playername\nGives GlobalChat information about a player.'), 10 | ('genable', 1, 'Enables GlobalChat.'), 11 | ('gdisable', 1, 'Disables GlobalChat.'), 12 | ('gmute', 1, 'Syntax: $playername $bantime $reason\nMutes a player in the GlobalChat\n$bantime: Negative values lead to perment mute. Otherwise use a timestring like "1d2h30s".'), 13 | ('gunmute', 1, 'Syntax: $playername\nUnmutes player in the GlobalChat.'), 14 | ('galliance', 1, 'Syntax: .galliance $text\nWrite a message in the Alliance GlobalChat.'), 15 | ('ghorde', 1, 'Syntax: .ghorde $text\nWrite a message in the Horde GlobalChat.'), 16 | ('gblacklist', 1, 'Type .gblacklist to see the list of possible subcommands or .help gblacklist $subcommand to see info on subcommands.'), 17 | ('gblacklist add', 1, 'Syntax: .gblacklist add $text\nAdds a phrase to the GlobalChat Profanity Blacklist.'), 18 | ('gblacklist remove', 1, 'Syntax: .gblacklist remove $text\nRemoves a phrase from the GlobalChat Profanity Blacklist.'), 19 | ('gblacklist reload', 1, 'Reloads the GlobalChat Profanity Blacklist.'); 20 | -------------------------------------------------------------------------------- /data/sql/db-world/base/globalchat_strings.sql: -------------------------------------------------------------------------------- 1 | SET @ENTRY := 17000; 2 | 3 | DELETE FROM `acore_string` WHERE `entry` IN (@ENTRY+0, @ENTRY+1, @ENTRY+2, @ENTRY+3, @ENTRY+4, @ENTRY+5,@ENTRY+6); 4 | INSERT INTO `acore_string` (`entry`, `content_default`, `locale_koKR`, `locale_frFR`, `locale_deDE`, `locale_zhCN`, `locale_zhTW`, `locale_esES`, `locale_esMX`, `locale_ruRU`) VALUES 5 | (@ENTRY+0, 'Player |cffff0000%s|r tried to post forbidden phrase: |cffff0000%s', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), 6 | (@ENTRY+1, 'Player |cffff0000%s|r tried to post URL: |cffff0000%s', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), 7 | (@ENTRY+2, '%s has |cffff0000%s|r the GlobalChat.|r', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), 8 | (@ENTRY+3, '%s has muted |cffff0000%s|r for %s in the GlobalChat.|r Reason: %s', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), 9 | (@ENTRY+4, '%s has permanently muted |cffff0000%s|r in the GlobalChat.|r Reason: %s', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), 10 | (@ENTRY+5, '|cffff0000You have been muted for %s in the GlobalChat.|r Reason: %s', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), 11 | (@ENTRY+6, '|cffff0000You have been permanently muted in the GlobalChat.|r Reason: %s', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); ; 12 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gozzim/mod-globalchat/256bdce29b05c5d94fd39b7214606a187a615789/icon.png -------------------------------------------------------------------------------- /images/AllianceChat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gozzim/mod-globalchat/256bdce29b05c5d94fd39b7214606a187a615789/images/AllianceChat.jpg -------------------------------------------------------------------------------- /images/ChatInfo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gozzim/mod-globalchat/256bdce29b05c5d94fd39b7214606a187a615789/images/ChatInfo.jpg -------------------------------------------------------------------------------- /images/ChatPrefix.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gozzim/mod-globalchat/256bdce29b05c5d94fd39b7214606a187a615789/images/ChatPrefix.jpg -------------------------------------------------------------------------------- /images/ClickChat.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gozzim/mod-globalchat/256bdce29b05c5d94fd39b7214606a187a615789/images/ClickChat.gif -------------------------------------------------------------------------------- /images/GMChat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gozzim/mod-globalchat/256bdce29b05c5d94fd39b7214606a187a615789/images/GMChat.jpg -------------------------------------------------------------------------------- /images/HordeChat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gozzim/mod-globalchat/256bdce29b05c5d94fd39b7214606a187a615789/images/HordeChat.png -------------------------------------------------------------------------------- /images/JoinChannel.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gozzim/mod-globalchat/256bdce29b05c5d94fd39b7214606a187a615789/images/JoinChannel.gif -------------------------------------------------------------------------------- /images/JoinLeaveGlobal.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gozzim/mod-globalchat/256bdce29b05c5d94fd39b7214606a187a615789/images/JoinLeaveGlobal.gif -------------------------------------------------------------------------------- /images/PlayerIcons.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gozzim/mod-globalchat/256bdce29b05c5d94fd39b7214606a187a615789/images/PlayerIcons.jpg -------------------------------------------------------------------------------- /images/PlayerName.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gozzim/mod-globalchat/256bdce29b05c5d94fd39b7214606a187a615789/images/PlayerName.jpg -------------------------------------------------------------------------------- /images/Profanities.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gozzim/mod-globalchat/256bdce29b05c5d94fd39b7214606a187a615789/images/Profanities.jpg -------------------------------------------------------------------------------- /images/Url.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gozzim/mod-globalchat/256bdce29b05c5d94fd39b7214606a187a615789/images/Url.jpg -------------------------------------------------------------------------------- /include.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | MOD_SPECTATOR_ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )/" && pwd )" 4 | 5 | source $MOD_SPECTATOR_ROOT"/conf/conf.sh.dist" 6 | 7 | if [ -f $MOD_SPECTATOR_ROOT"/conf/conf.sh" ]; then 8 | source $MOD_SPECTATOR_ROOT"/conf/conf.sh" 9 | fi 10 | -------------------------------------------------------------------------------- /src/GlobalChat.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by the 6 | * Free Software Foundation; either version 3 of the License, or (at your 7 | * option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for 12 | * more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program. If not, see . 16 | */ 17 | 18 | void AddSC_GlobalChat(); 19 | void AddSC_globalchat_commandscript(); 20 | 21 | void Addmod_globalchatScripts() 22 | { 23 | AddSC_GlobalChat(); 24 | AddSC_globalchat_commandscript(); 25 | } 26 | -------------------------------------------------------------------------------- /src/GlobalChatData.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by the 6 | * Free Software Foundation; either version 3 of the License, or (at your 7 | * option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for 12 | * more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program. If not, see . 16 | */ 17 | 18 | #include "GlobalChatData.h" 19 | 20 | GlobalChatData::GlobalChatData() 21 | { 22 | inChat = sGlobalChatMgr->EnableOnLogin; 23 | lastMessage = 0; 24 | muteTime = 0; 25 | totalMutes = 0; 26 | banned = false; 27 | } 28 | 29 | GlobalChatData::~GlobalChatData() 30 | { 31 | } 32 | 33 | bool GlobalChatData::IsInChat() const 34 | { 35 | return inChat; 36 | } 37 | 38 | void GlobalChatData::SetInChat(bool _inChat) 39 | { 40 | inChat = _inChat; 41 | } 42 | 43 | time_t GlobalChatData::GetLastMessage() const 44 | { 45 | return lastMessage; 46 | } 47 | 48 | void GlobalChatData::SetLastMessage(time_t _lastMessage) 49 | { 50 | lastMessage = _lastMessage; 51 | } 52 | 53 | time_t GlobalChatData::GetMuteTime() const 54 | { 55 | return muteTime; 56 | } 57 | 58 | void GlobalChatData::SetMuteTime(time_t _muteTime) 59 | { 60 | muteTime = _muteTime; 61 | } 62 | 63 | uint32 GlobalChatData::GetTotalMutes() const 64 | { 65 | return totalMutes; 66 | } 67 | 68 | void GlobalChatData::SetTotalMutes(uint32 _totalMutes) 69 | { 70 | totalMutes = _totalMutes; 71 | } 72 | 73 | bool GlobalChatData::IsBanned() const 74 | { 75 | return banned; 76 | } 77 | 78 | void GlobalChatData::SetBanned(bool _banned) 79 | { 80 | banned = _banned; 81 | } -------------------------------------------------------------------------------- /src/GlobalChatData.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by the 6 | * Free Software Foundation; either version 3 of the License, or (at your 7 | * option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for 12 | * more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program. If not, see . 16 | */ 17 | 18 | #ifndef _GLOBALCHATDATA_H_ 19 | #define _GLOBALCHATDATA_H_ 20 | 21 | #include "GlobalChatMgr.h" 22 | 23 | class GlobalChatData 24 | { 25 | public: 26 | GlobalChatData(); 27 | ~GlobalChatData(); 28 | 29 | bool IsInChat() const; 30 | void SetInChat(bool _inChat); 31 | 32 | time_t GetLastMessage() const; 33 | void SetLastMessage(time_t _lastMessage); 34 | 35 | time_t GetMuteTime() const; 36 | void SetMuteTime(time_t _muteTime); 37 | 38 | uint32 GetTotalMutes() const; 39 | void SetTotalMutes(uint32 _totalMutes); 40 | 41 | bool IsBanned() const; 42 | void SetBanned(bool _banned); 43 | 44 | private: 45 | bool inChat; 46 | time_t lastMessage; 47 | time_t muteTime; 48 | uint32 totalMutes; 49 | bool banned; 50 | }; 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /src/GlobalChatMgr.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by the 6 | * Free Software Foundation; either version 3 of the License, or (at your 7 | * option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for 12 | * more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program. If not, see . 16 | */ 17 | 18 | #include "Channel.h" 19 | #include "Config.h" 20 | #include "DatabaseEnv.h" 21 | #include "GameTime.h" 22 | #include "World.h" 23 | #include "GlobalChatMgr.h" 24 | #include "WorldSession.h" 25 | 26 | DBCStorage sChatProfanityStore(ChatProfanityEntryfmt); 27 | 28 | GlobalChatMgr* GlobalChatMgr::instance() 29 | { 30 | static GlobalChatMgr instance; 31 | return &instance; 32 | } 33 | 34 | void GlobalChatMgr::LoadConfig(bool reload) 35 | { 36 | GlobalChatEnabled = sConfigMgr->GetOption("GlobalChat.Enable", true); 37 | Announce = sConfigMgr->GetOption("GlobalChat.Announce", true); 38 | ChatName = sConfigMgr->GetOption("GlobalChat.Chat.Name", "Global"); 39 | ChatNameColor = sConfigMgr->GetOption("GlobalChat.Chat.NameColor", "FFFF00"); 40 | ChatTextColor = sConfigMgr->GetOption("GlobalChat.Chat.TextColor", "FFFFFF"); 41 | PlayerColor = sConfigMgr->GetOption("GlobalChat.Player.NameColor", 1); 42 | FactionIcon = sConfigMgr->GetOption("GlobalChat.Player.FactionIcon", false); 43 | RaceIcon = sConfigMgr->GetOption("GlobalChat.Player.RaceIcon", true); 44 | ClassIcon = sConfigMgr->GetOption("GlobalChat.Player.ClassIcon", false); 45 | GMBadge = sConfigMgr->GetOption("GlobalChat.GM.Badge", 1); 46 | FactionSpecific = sConfigMgr->GetOption("GlobalChat.FactionSpecific", false); 47 | EnableOnLogin = sConfigMgr->GetOption("GlobalChat.OnFirstLogin", true); 48 | MinPlayTime = sConfigMgr->GetOption("GlobalChat.PlayTimeToChat", 300); 49 | CoolDown = sConfigMgr->GetOption("GlobalChat.CoolDown", 2); 50 | SendIgnored = sConfigMgr->GetOption("GlobalChat.SendToIgnored", false); 51 | JoinChannel = sConfigMgr->GetOption("GlobalChat.JoinChannelAllowed", false); 52 | AnnounceMutes = sConfigMgr->GetOption("GlobalChat.AnnounceMutes", false); 53 | ProfanityBlockType = sConfigMgr->GetOption("GlobalChat.Profanity.BlockType", 1); 54 | ProfanityBlockLevel = sConfigMgr->GetOption("GlobalChat.Profanity.BlockLevel", 0); 55 | ProfanityMuteType = sConfigMgr->GetOption("GlobalChat.Profanity.MuteType", 1); 56 | ProfanityMute = sConfigMgr->GetOption("GlobalChat.Profanity.MuteTime", 30); 57 | ProfanityFromDBC = sConfigMgr->GetOption("GlobalChat.Profanity.FromDBC", false); 58 | URLBlockType = sConfigMgr->GetOption("GlobalChat.URL.BlockType", 1); 59 | URLBlockLevel = sConfigMgr->GetOption("GlobalChat.URL.BlockLevel", 0); 60 | URLMuteType = sConfigMgr->GetOption("GlobalChat.URL.MuteType", 1); 61 | URLMute = sConfigMgr->GetOption("GlobalChat.URL.MuteTime", 120); 62 | 63 | // Checking Hex ColorCodes for validity 64 | if (!isValidHexColorCode(ChatNameColor)) 65 | { 66 | LOG_ERROR("module", "GlobalChat: ChatNameColor is not a valid HexColorCode - Falling back to default"); 67 | ChatNameColor = "FFFF00"; 68 | } 69 | 70 | if (!isValidHexColorCode(ChatTextColor)) 71 | { 72 | LOG_ERROR("module", "GlobalChat: ChatTextColor is not a valid HexColorCode - Falling back to default"); 73 | ChatTextColor = "FFFFFF"; 74 | } 75 | 76 | if (reload) 77 | { 78 | GMColors.clear(); 79 | ProfanityBlacklist.clear(); 80 | URLWhitelist.clear(); 81 | } 82 | 83 | std::string configColors = sConfigMgr->GetOption("GlobalChat.GM.Colors", "00FF00;091FE0;FF0000"); 84 | std::string color; 85 | std::istringstream colors(configColors); 86 | while (std::getline(colors, color, ';')) 87 | { 88 | LOG_DEBUG("module", "GlobalChat: GMColor at position '{}' has length {} and value '{}' - isEmpty: {}", GMColors.size(), color.size(), color, color.empty()); 89 | if (color.empty()) 90 | { 91 | LOG_WARN("module", "GlobalChat: GMColor at position '{}' is empty - Falling back to configured PlayerNameColor", GMColors.size()); 92 | GMColors.push_back(color); 93 | continue; 94 | } 95 | 96 | if (!isValidHexColorCode(color)) 97 | { 98 | LOG_ERROR("module", "GlobalChat: GMColor at position '{}' is not a valid HexColorCode - Falling back to default GM Colors", GMColors.size()); 99 | switch (GMColors.size()) 100 | { 101 | case 0: 102 | GMColors.push_back("00FF00"); 103 | break; 104 | case 1: 105 | GMColors.push_back("091FE0"); 106 | break; 107 | default: 108 | GMColors.push_back("FF0000"); 109 | break; 110 | } 111 | continue; 112 | } 113 | 114 | GMColors.push_back(color); 115 | } 116 | 117 | // Do not remove these lines 118 | GMColors.insert(GMColors.begin(), ChatNameColor); 119 | GMColors.push_back("808080"); 120 | 121 | LoadBlacklistDB(); 122 | if (ProfanityFromDBC) 123 | LoadProfanityDBC(); 124 | 125 | std::string configUrl = sConfigMgr->GetOption("GlobalChat.URL.Whitelist", ""); 126 | std::string url; 127 | std::istringstream urls(configUrl); 128 | while (std::getline(urls, url, ';')) 129 | { 130 | URLWhitelist.push_back(url); 131 | } 132 | } 133 | 134 | void GlobalChatMgr::LoadPlayerData(Player* player) 135 | { 136 | ObjectGuid guid = player->GetGUID(); 137 | 138 | QueryResult result = CharacterDatabase.Query("SELECT enabled,last_msg,mute_time,total_mutes,banned FROM player_globalchat_status WHERE guid={};", guid.GetCounter()); 139 | 140 | if (!result) 141 | return; 142 | 143 | if (result->GetRowCount() == 0) 144 | return; 145 | 146 | Field* fields = result->Fetch(); 147 | 148 | bool enabled = fields[0].Get(); 149 | time_t lastMessage = time_t(fields[1].Get()); 150 | time_t muteTime = time_t(fields[2].Get()); 151 | uint32 totalMutes = fields[3].Get(); 152 | bool banned = fields[4].Get(); 153 | 154 | playersChatData[guid].SetInChat(enabled); 155 | playersChatData[guid].SetLastMessage(lastMessage); 156 | playersChatData[guid].SetMuteTime(muteTime); 157 | playersChatData[guid].SetTotalMutes(totalMutes); 158 | playersChatData[guid].SetBanned(banned); 159 | } 160 | 161 | void GlobalChatMgr::SavePlayerData(Player* player) 162 | { 163 | LOG_DEBUG("module", "GlobalChat: Saving PlayerData for {}", player->GetName()); 164 | ObjectGuid guid = player->GetGUID(); 165 | CharacterDatabase.Execute("REPLACE INTO player_globalchat_status (guid,enabled,last_msg,mute_time,total_mutes,banned) VALUES ({},{},{},{},{},{});", guid.GetCounter(), playersChatData[guid].IsInChat(), playersChatData[guid].GetLastMessage(), playersChatData[guid].GetMuteTime(), playersChatData[guid].GetTotalMutes(), playersChatData[guid].IsBanned()); 166 | } 167 | 168 | void GlobalChatMgr::LoadBlacklistDB() 169 | { 170 | QueryResult blacklist = CharacterDatabase.Query("SELECT phrase FROM `globalchat_blacklist`"); 171 | 172 | if (!blacklist) 173 | return; 174 | 175 | do 176 | { 177 | Field* field = blacklist->Fetch(); 178 | std::string phrase = field[0].Get(); 179 | ProfanityBlacklist[phrase] = std::regex{phrase, std::regex::icase | std::regex::optimize}; 180 | } while (blacklist->NextRow()); 181 | } 182 | 183 | void GlobalChatMgr::LoadProfanityDBC() 184 | { 185 | LOG_DEBUG("module", "GlobalChat: Loading ProfanityDBC"); 186 | 187 | uint32 availableDbcLocales = 0xFFFFFFFF; 188 | 189 | std::string filename = "ChatProfanity.dbc"; 190 | std::string dbcPath = sWorld->GetDataPath() + "dbc/"; 191 | std::string dbcFilename = dbcPath + filename; 192 | 193 | if (sChatProfanityStore.Load(dbcFilename.c_str())) 194 | { 195 | for (uint8 i = 0; i < TOTAL_LOCALES; ++i) 196 | { 197 | if (!(availableDbcLocales & (1 << i))) 198 | continue; 199 | 200 | std::string localizedName(dbcPath); 201 | localizedName.append(localeNames[i]); 202 | localizedName.push_back('/'); 203 | localizedName.append(filename); 204 | 205 | if (!sChatProfanityStore.LoadStringsFrom(localizedName.c_str())) 206 | availableDbcLocales &= ~(1 << i); // mark as not available for speedup next checks 207 | } 208 | } 209 | 210 | for (ChatProfanityEntry const* chatProfanity : sChatProfanityStore) 211 | { 212 | std::string text = chatProfanity->Text; 213 | 214 | text.erase(remove(text.begin(), text.end(), '{'), text.end()); 215 | text.erase(remove(text.begin(), text.end(), '}'), text.end()); 216 | text.erase(remove(text.begin(), text.end(), '\\'), text.end()); 217 | text.erase(remove(text.begin(), text.end(), '<'), text.end()); 218 | text.erase(remove(text.begin(), text.end(), '>'), text.end()); 219 | 220 | ProfanityBlacklist[text] = std::regex{text, std::regex::icase | std::regex::optimize}; 221 | } 222 | } 223 | 224 | bool GlobalChatMgr::IsInChat(ObjectGuid guid) 225 | { 226 | return playersChatData[guid].IsInChat(); 227 | } 228 | 229 | bool GlobalChatMgr::isValidHexColorCode(std::string color) 230 | { 231 | if (color.length() != 6 ) 232 | return false; 233 | 234 | for (const char& c : color) 235 | { 236 | if (!isxdigit(c)) 237 | return false; 238 | } 239 | 240 | return true; 241 | } 242 | 243 | void GlobalChatMgr::Mute(ObjectGuid guid, uint32 duration) 244 | { 245 | int64 muteTime = GameTime::GetGameTime().count() + duration; 246 | playersChatData[guid].SetMuteTime(muteTime); 247 | uint32 totalMutes = playersChatData[guid].GetTotalMutes(); 248 | playersChatData[guid].SetTotalMutes(totalMutes + 1); 249 | } 250 | 251 | void GlobalChatMgr::Ban(ObjectGuid guid) 252 | { 253 | playersChatData[guid].SetBanned(true); 254 | uint32 totalMutes = playersChatData[guid].GetTotalMutes(); 255 | playersChatData[guid].SetTotalMutes(totalMutes++); 256 | } 257 | 258 | void GlobalChatMgr::Unmute(ObjectGuid guid) 259 | { 260 | playersChatData[guid].SetBanned(false); 261 | playersChatData[guid].SetMuteTime(0); 262 | } 263 | 264 | bool GlobalChatMgr::HasForbiddenPhrase(std::string message) 265 | { 266 | for (auto const& regex : ProfanityBlacklist) 267 | if (std::regex_search(message, regex.second)) 268 | return true; 269 | 270 | return false; 271 | } 272 | 273 | bool GlobalChatMgr::HasForbiddenURL(std::string message) 274 | { 275 | auto words_begin = std::sregex_iterator(message.begin(), message.end(), urlRegex); 276 | auto words_end = std::sregex_iterator(); 277 | 278 | for (std::sregex_iterator i = words_begin; i != words_end; ++i) 279 | { 280 | std::smatch match = *i; 281 | 282 | if (std::find(URLWhitelist.begin(), URLWhitelist.end(), match[3].str()) != URLWhitelist.end()) 283 | continue; 284 | 285 | return true; 286 | } 287 | 288 | return false; 289 | } 290 | 291 | std::string GlobalChatMgr::CensorForbiddenPhrase(std::string message) 292 | { 293 | std::ostringstream result; 294 | std::smatch match; 295 | 296 | for (auto const& regex : ProfanityBlacklist) 297 | { 298 | if (std::regex_search(message, match, regex.second)) 299 | { 300 | result << match.prefix(); 301 | 302 | if (match.str().size() > 0) 303 | { 304 | result << std::string(match.str().size(), '*'); 305 | } 306 | 307 | return result.str() + CensorForbiddenPhrase(match.suffix()); 308 | } 309 | } 310 | 311 | return message; 312 | } 313 | 314 | std::string GlobalChatMgr::CensorForbiddenURL(std::string message) 315 | { 316 | std::ostringstream result; 317 | std::smatch match; 318 | 319 | if (std::regex_search(message, match, urlRegex)) { 320 | result << match.prefix(); 321 | 322 | if (std::find(URLWhitelist.begin(), URLWhitelist.end(), match[5].str()) != URLWhitelist.end()) 323 | { 324 | result << match.str(); 325 | } 326 | else 327 | { 328 | if (match[8].str().size() > 0) 329 | result << std::string(match[8].str().size(), '*'); 330 | 331 | if (match[6].str().size() > 0) 332 | { 333 | result << match[1].str() << match[4].str(); 334 | result << std::string(match[6].str().size(), '*'); 335 | result << match[7].str(); 336 | } 337 | } 338 | 339 | return result.str() + CensorForbiddenURL(match.suffix()); 340 | } 341 | 342 | return message; 343 | } 344 | 345 | std::string GlobalChatMgr::GetFactionIcon(Player* player) 346 | { 347 | switch (player->GetTeamId()) 348 | { 349 | case TEAM_ALLIANCE: 350 | return "|TInterface\\ICONS\\Achievement_PVP_A_A:13:13:0:-3|t"; 351 | case TEAM_HORDE: 352 | return "|TInterface\\ICONS\\Achievement_PVP_H_H:13:13:0:-3|t"; 353 | default: 354 | return ""; 355 | } 356 | } 357 | 358 | std::string GlobalChatMgr::GetFactionColor(Player* player) 359 | { 360 | switch (player->GetTeamId()) 361 | { 362 | case TEAM_ALLIANCE: 363 | return "3399FF"; 364 | case TEAM_HORDE: 365 | return "CC0000"; 366 | default: 367 | return "FFFFFF"; 368 | } 369 | } 370 | 371 | std::string GlobalChatMgr::GetClassIcon(Player* player) 372 | { 373 | std::ostringstream icon; 374 | 375 | uint8 iconSize = 32; 376 | uint8 row = 0; 377 | uint8 column = 0; 378 | switch (player->getClass()) 379 | { 380 | case CLASS_WARRIOR: 381 | row = 0; 382 | column = 0; 383 | break; 384 | case CLASS_MAGE: 385 | row = 0; 386 | column = 1; 387 | break; 388 | case CLASS_ROGUE: 389 | row = 0; 390 | column = 2; 391 | break; 392 | case CLASS_DRUID: 393 | row = 0; 394 | column = 3; 395 | break; 396 | case CLASS_HUNTER: 397 | row = 1; 398 | column = 0; 399 | break; 400 | case CLASS_SHAMAN: 401 | row = 1; 402 | column = 1; 403 | break; 404 | case CLASS_PRIEST: 405 | row = 1; 406 | column = 2; 407 | break; 408 | case CLASS_WARLOCK: 409 | row = 1; 410 | column = 3; 411 | break; 412 | case CLASS_PALADIN: 413 | row = 2; 414 | column = 0; 415 | break; 416 | case CLASS_DEATH_KNIGHT: 417 | row = 2; 418 | column = 1; 419 | break; 420 | } 421 | 422 | icon << "|TInterface\\Glues\\CharacterCreate\\UI-CharacterCreate-Classes:13:13:0:-3:128:128:"; 423 | icon << column * iconSize << ":" << (column + 1) * iconSize << ":"; 424 | icon << row * iconSize << ":" << (row + 1) * iconSize << "|t"; 425 | 426 | return icon.str(); 427 | } 428 | 429 | std::string GlobalChatMgr::GetClassColor(Player* player) 430 | { 431 | switch (player->getClass()) 432 | { 433 | case CLASS_DEATH_KNIGHT: 434 | return "C41F3B"; 435 | case CLASS_DRUID: 436 | return "FF7D0A"; 437 | case CLASS_HUNTER: 438 | return "ABD473"; 439 | case CLASS_MAGE: 440 | return "69CCF0"; 441 | case CLASS_PALADIN: 442 | return "F58CBA"; 443 | case CLASS_PRIEST: 444 | return "FFFFFF"; 445 | case CLASS_ROGUE: 446 | return "FFF569"; 447 | case CLASS_SHAMAN: 448 | return "0070DE"; 449 | case CLASS_WARLOCK: 450 | return "9482C9"; 451 | case CLASS_WARRIOR: 452 | return "C79C6E"; 453 | default: 454 | return GMColors[0]; 455 | } 456 | } 457 | 458 | std::string GlobalChatMgr::GetRaceIcon(Player* player) 459 | { 460 | std::ostringstream icon; 461 | 462 | uint8 iconSize = 32; 463 | uint8 row = 0; 464 | uint8 column = 0; 465 | 466 | switch (player->getRace()) 467 | { 468 | case RACE_HUMAN: 469 | row = 0; 470 | column = 0; 471 | break; 472 | case RACE_DWARF: 473 | row = 0; 474 | column = 1; 475 | break; 476 | case RACE_GNOME: 477 | row = 0; 478 | column = 2; 479 | break; 480 | case RACE_NIGHTELF: 481 | row = 0; 482 | column = 3; 483 | break; 484 | case RACE_DRAENEI: 485 | row = 0; 486 | column = 4; 487 | break; 488 | case RACE_TAUREN: 489 | row = 1; 490 | column = 0; 491 | break; 492 | case RACE_UNDEAD_PLAYER: 493 | row = 1; 494 | column = 1; 495 | break; 496 | case RACE_TROLL: 497 | row = 1; 498 | column = 2; 499 | break; 500 | case RACE_ORC: 501 | row = 1; 502 | column = 3; 503 | break; 504 | case RACE_BLOODELF: 505 | row = 1; 506 | column = 4; 507 | break; 508 | } 509 | 510 | if (player->getGender() == GENDER_FEMALE) 511 | { 512 | row += 2; 513 | } 514 | 515 | icon << "|TInterface\\Glues\\CharacterCreate\\UI-CharacterCreate-Races:13:13:0:-3:256:128:"; 516 | icon << column * iconSize << ":" << (column + 1) * iconSize << ":"; 517 | icon << row * iconSize << ":" << (row + 1) * iconSize << "|t"; 518 | 519 | return icon.str(); 520 | } 521 | 522 | std::string GlobalChatMgr::GetChatPrefix() 523 | { 524 | std::ostringstream chatPrefix; 525 | 526 | if (!ChatName.empty()) 527 | { 528 | chatPrefix << "|Hchannel:"; 529 | if (JoinChannel) 530 | { 531 | chatPrefix << "c " << ChatName; 532 | } 533 | else 534 | { 535 | chatPrefix << "s .global "; 536 | } 537 | chatPrefix << "|h|cff" << (ChatNameColor.empty() ? "FFFF00" : ChatNameColor); 538 | chatPrefix << "[" << ChatName << "]|h"; 539 | } 540 | 541 | return chatPrefix.str(); 542 | } 543 | 544 | std::string GlobalChatMgr::GetGMChatPrefix(TeamId teamId) 545 | { 546 | std::ostringstream chatPrefix; 547 | std::string factionColor = teamId == TEAM_ALLIANCE ? "3399FF" : "CC0000"; 548 | std::string factionName = teamId == TEAM_ALLIANCE ? "Alliance" : "Horde"; 549 | std::string factionCommand = teamId == TEAM_ALLIANCE ? ".galliance" : ".ghorde"; 550 | 551 | chatPrefix << "|Hchannel:"; 552 | chatPrefix << "s " << factionCommand << " "; 553 | chatPrefix << "|h|cff" << factionColor; 554 | chatPrefix << "[" << factionName << "]|h"; 555 | chatPrefix << "|cff" << (ChatNameColor.empty() ? "FFFF00" : ChatNameColor); 556 | 557 | return chatPrefix.str(); 558 | } 559 | 560 | std::string GlobalChatMgr::GetNameLink(Player* player) 561 | { 562 | std::ostringstream nameLink; 563 | 564 | if (!player) 565 | { 566 | nameLink << "[|cff" << (GMColors.size() > 0 ? GMColors.back() : "808080"); 567 | nameLink << "Console"; 568 | nameLink << "|cff" << (ChatNameColor.empty() ? "FFFF00" : ChatNameColor) << "]"; 569 | return nameLink.str(); 570 | } 571 | 572 | std::string playerName = player->GetName(); 573 | AccountTypes playerSecurity = player->GetSession()->GetSecurity(); 574 | 575 | std::string color; 576 | std::string icons; 577 | 578 | if (FactionIcon) 579 | icons += GetFactionIcon(player); 580 | 581 | if (RaceIcon) 582 | icons += GetRaceIcon(player); 583 | 584 | if (ClassIcon) 585 | icons += GetClassIcon(player); 586 | 587 | switch (PlayerColor) 588 | { 589 | case 1: 590 | color = GetClassColor(player); 591 | break; 592 | case 2: 593 | color = GetFactionColor(player); 594 | break; 595 | default: 596 | color = GMColors[0]; 597 | break; 598 | } 599 | 600 | if (playerSecurity > 0 && (GMBadge == 1 || (GMBadge == 2 && (player->IsDeveloper() || player->IsGameMaster())) || (GMBadge == 3 && player->isGMChat()))) 601 | { 602 | icons = "|TINTERFACE\\CHATFRAME\\UI-CHATICON-BLIZZ:12:22:0:-3|t"; 603 | 604 | if (GMColors.size() > 2 && playerSecurity < GMColors.size()) 605 | { 606 | std::string gmColor = GMColors[playerSecurity]; 607 | if (isValidHexColorCode(gmColor)) 608 | { 609 | color = gmColor; 610 | } 611 | } 612 | } 613 | 614 | nameLink << icons; 615 | nameLink << "[|Hplayer:" << playerName << "|h"; 616 | nameLink << "|cff" << color << playerName << "|h"; 617 | nameLink << "|cff" << (ChatNameColor.empty() ? "FFFF00" : ChatNameColor) << "]"; 618 | 619 | return nameLink.str(); 620 | } 621 | 622 | std::string GlobalChatMgr::BuildChatContent(std::string text) 623 | { 624 | std::string content = text; 625 | std::string color = ChatTextColor.empty() ? "|cffFFFFFF" : "|cff" + ChatTextColor; 626 | 627 | // Find and replace any resets of the color (e.g. from linking an Item) and set the color to ChatTextColor 628 | if (content.find("|H") != std::string::npos && content.find("|h") != std::string::npos && content.find("|cff") != std::string::npos) 629 | { 630 | size_t pos = 0; 631 | while ((pos = content.find("|h|r", pos)) != std::string::npos) 632 | { 633 | pos = content.find("|r", pos); 634 | content.replace(pos, 2, color); 635 | pos += 9; 636 | } 637 | } 638 | 639 | return content; 640 | } 641 | 642 | void GlobalChatMgr::SendToPlayers(std::string chatMessage, Player* player, TeamId teamId) 643 | { 644 | LOG_DEBUG("module", "GlobalChat: Sending Message to Players."); 645 | std::string chatPrefix = GetChatPrefix(); 646 | std::string gmChatPrefix = GetGMChatPrefix(teamId); 647 | SessionMap sessions = sWorld->GetAllSessions(); 648 | for (SessionMap::iterator itr = sessions.begin(); itr != sessions.end(); ++itr) 649 | { 650 | if (!itr->second || !itr->second->GetPlayer() || !itr->second->GetPlayer()->IsInWorld()) 651 | { 652 | continue; 653 | } 654 | 655 | Player* target = itr->second->GetPlayer(); 656 | ObjectGuid guid2 = target->GetGUID(); 657 | std::string message; 658 | 659 | if (IsInChat(guid2)) 660 | { 661 | if (FactionSpecific && teamId != TEAM_NEUTRAL && itr->second->GetSecurity() > 0) 662 | { 663 | message = gmChatPrefix + " " + chatMessage; 664 | sWorld->SendServerMessage(SERVER_MSG_STRING, message.c_str(), target); 665 | continue; 666 | } 667 | 668 | // Skip if receiver has sender on ignore 669 | if (player && !SendIgnored && target->GetSocial()->HasIgnore(player->GetGUID()) && player->GetSession()->GetSecurity() == 0) 670 | continue; 671 | 672 | if (!FactionSpecific || teamId == TEAM_NEUTRAL || teamId == target->GetTeamId()) 673 | { 674 | message = chatPrefix + " " + chatMessage; 675 | sWorld->SendServerMessage(SERVER_MSG_STRING, message.c_str(), target); 676 | } 677 | } 678 | } 679 | } 680 | 681 | void GlobalChatMgr::SendGlobalChat(WorldSession* session, const char* message, TeamId toTeam) 682 | { 683 | Player* player; 684 | 685 | std::string nameLink; 686 | std::string chatText = message; 687 | std::string chatContent; 688 | 689 | std::string chatColor = ChatTextColor.empty() ? "FFFFFF" : ChatTextColor; 690 | std::ostringstream chat_stream; 691 | 692 | if (!session) 693 | { 694 | nameLink = GetNameLink(nullptr); 695 | chatContent = BuildChatContent(chatText); 696 | 697 | chat_stream << nameLink << ": "; 698 | chat_stream << "|cff" << chatColor; 699 | chat_stream << chatContent; 700 | 701 | SendToPlayers(chat_stream.str(), nullptr, TEAM_NEUTRAL); 702 | return; 703 | } 704 | 705 | player = session->GetPlayer(); 706 | nameLink = GetNameLink(player); 707 | 708 | ObjectGuid guid = player->GetGUID(); 709 | const char* playerName = player->GetName().c_str(); 710 | AccountTypes playerSecurity = session->GetSecurity(); 711 | 712 | // Prevent Spamming first to avoid sending massive amounts of SysMessages as well 713 | if (playersChatData[guid].GetLastMessage() + CoolDown >= GameTime::GetGameTime().count() && playerSecurity == 0) 714 | { 715 | return; 716 | } 717 | 718 | if (playerSecurity == 0 && !GlobalChatEnabled) 719 | { 720 | ChatHandler(session).PSendSysMessage("|cffff0000GlobalChat is currently disabled.|r"); 721 | return; 722 | } 723 | 724 | if (playersChatData[guid].IsBanned()) 725 | { 726 | ChatHandler(session).PSendSysMessage("|cffff0000You are currently banned from the GlobalChat.|r"); 727 | return; 728 | } 729 | 730 | if (!player->CanSpeak() || playersChatData[guid].GetMuteTime() > GameTime::GetGameTime().count()) 731 | { 732 | uint32 muteLeft = session->m_muteTime - GameTime::GetGameTime().count(); 733 | if (playersChatData[guid].GetMuteTime() > session->m_muteTime) 734 | { 735 | muteLeft = playersChatData[guid].GetMuteTime() - GameTime::GetGameTime().count(); 736 | } 737 | 738 | ChatHandler(session).PSendSysMessage("|cffff0000You can't use the GlobalChat while muted.|r You need to wait another %s.", secsToTimeString(muteLeft)); 739 | return; 740 | } 741 | 742 | if (!IsInChat(guid)) 743 | { 744 | ChatHandler(session).PSendSysMessage("|cffff0000You have not joined the GlobalChat. Type |r.joinglobal|cffff0000 to join the GlobalChat.|r"); 745 | return; 746 | } 747 | 748 | if (chatText.empty()) 749 | { 750 | ChatHandler(session).PSendSysMessage("Your message cannot be empty."); 751 | return; 752 | } 753 | 754 | if (ProfanityBlockType > 0 && playerSecurity <= ProfanityBlockLevel && HasForbiddenPhrase(message)) 755 | { 756 | if (ProfanityBlockType == 1) 757 | { 758 | chatText = CensorForbiddenPhrase(chatText); 759 | } 760 | 761 | if ((playerSecurity > 0 && ProfanityBlockType != 1) || ProfanityBlockType == 2) 762 | { 763 | ChatHandler(session).PSendSysMessage("Your message contains a forbidden phrase."); 764 | LOG_INFO("module", "GlobalChat: Player {} tried posting a forbidden message.", player->GetName()); 765 | return; 766 | } 767 | 768 | if (ProfanityBlockType == 3) 769 | { 770 | if (ProfanityMute > 0) 771 | { 772 | sWorld->SendGMText(LANG_FORBIDDEN_PHRASE_ANNOUNCE_GM, playerName, message); // send report to GMs 773 | LOG_INFO("module", "GlobalChat: Player {} got muted for {} for posting a forbidden message.", player->GetName(), secsToTimeString(ProfanityMute)); 774 | ChatHandler(session).PSendSysMessage("Your message contains a forbidden phrase. You have been muted for %s.", secsToTimeString(ProfanityMute)); 775 | 776 | if (ProfanityMuteType >= 1) 777 | { 778 | Mute(guid, ProfanityMute); 779 | } 780 | 781 | if (ProfanityMuteType >= 2) 782 | { 783 | int64 muteTime = GameTime::GetGameTime().count() + ProfanityMute; 784 | LoginDatabasePreparedStatement* mt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_MUTE_TIME); 785 | session->m_muteTime = muteTime; 786 | mt->SetData(0, muteTime); 787 | } 788 | } 789 | else 790 | { 791 | sWorld->SendGMText(LANG_FORBIDDEN_PHRASE_ANNOUNCE_GM, playerName, message); // send report to GMs 792 | LOG_INFO("module", "GlobalChat: Player {} tried posting a forbidden message.", player->GetName()); 793 | ChatHandler(session).PSendSysMessage("Your message contains a forbidden phrase."); 794 | } 795 | 796 | return; 797 | } 798 | } 799 | 800 | if (URLBlockType > 0 && playerSecurity <= URLBlockLevel && HasForbiddenURL(message)) 801 | { 802 | if (URLBlockType == 1) 803 | { 804 | chatText = CensorForbiddenURL(chatText); 805 | } 806 | 807 | if ((playerSecurity > 0 && URLBlockType != 1) || URLBlockType == 2) 808 | { 809 | ChatHandler(session).PSendSysMessage("Urls are not allowed."); 810 | LOG_INFO("module", "GlobalChat: Player {} tried posting a forbidden URL.", player->GetName()); 811 | return; 812 | } 813 | 814 | if (URLBlockType == 3) 815 | { 816 | if (URLMute > 0) 817 | { 818 | sWorld->SendGMText(LANG_FORBIDDEN_URL_ANNOUNCE_GM, playerName, message); // send passive report to GMs 819 | LOG_INFO("module", "GlobalChat: Player {} got muted for {} for posting a forbidden URL.", player->GetName(), secsToTimeString(URLMute)); 820 | ChatHandler(session).PSendSysMessage("Urls are not allowed. You have been muted for %s.", secsToTimeString(URLMute)); 821 | 822 | if (URLMuteType >= 1) 823 | { 824 | Mute(guid, URLMute); 825 | } 826 | 827 | if (URLMuteType >= 2) 828 | { 829 | int64 muteTime = GameTime::GetGameTime().count() + URLMute; 830 | LoginDatabasePreparedStatement* mt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_MUTE_TIME); 831 | session->m_muteTime = muteTime; 832 | mt->SetData(0, muteTime); 833 | } 834 | } 835 | else 836 | { 837 | sWorld->SendGMText(LANG_FORBIDDEN_URL_ANNOUNCE_GM, playerName, message); // send passive report to GMs 838 | LOG_INFO("module", "GlobalChat: Player {} tried posting a forbidden URL.", player->GetName()); 839 | ChatHandler(session).PSendSysMessage("Urls are not allowed."); 840 | return; 841 | } 842 | 843 | return; 844 | } 845 | } 846 | 847 | if (player->GetTotalPlayedTime() <= MinPlayTime && session->GetSecurity() == 0) 848 | { 849 | std::string adStr = secsToTimeString(MinPlayTime - player->GetTotalPlayedTime()); 850 | std::string minTime = secsToTimeString(MinPlayTime); 851 | session->SendNotification("You must have played at least %s to use the GlobalChat. %s remaining.", minTime.c_str(), adStr.c_str()); 852 | return; 853 | } 854 | 855 | // Build Chat Content from text 856 | chatContent = BuildChatContent(chatText); 857 | 858 | // Update last message to avoid sending massive amounts of SysMessages as well 859 | playersChatData[guid].SetLastMessage(GameTime::GetGameTime().count()); 860 | 861 | chat_stream << nameLink << ": "; 862 | chat_stream << "|cff" << chatColor; 863 | chat_stream << chatContent; 864 | 865 | LOG_INFO("module", "GlobalChat: Player {}: {}", player->GetName(), chatText); 866 | if (toTeam != TEAM_NEUTRAL) 867 | SendToPlayers(chat_stream.str(), player, toTeam); 868 | else 869 | SendToPlayers(chat_stream.str(), player, player->GetTeamId()); 870 | } 871 | 872 | void GlobalChatMgr::PlayerJoinCommand(ChatHandler* handler) 873 | { 874 | Player* player = handler->GetSession()->GetPlayer(); 875 | ObjectGuid guid = player->GetGUID(); 876 | 877 | if (!GlobalChatEnabled) 878 | { 879 | handler->PSendSysMessage("The GlobalChat is currently disabled."); 880 | return; 881 | } 882 | 883 | if (IsInChat(guid)) 884 | { 885 | handler->PSendSysMessage("You already joined the GlobalChat."); 886 | return; 887 | } 888 | 889 | playersChatData[guid].SetInChat(true); 890 | 891 | handler->PSendSysMessage("You have joined the GlobalChat."); 892 | LOG_INFO("module", "GlobalChat: Player {} joined GlobalChat.", player->GetName()); 893 | } 894 | 895 | void GlobalChatMgr::PlayerLeaveCommand(ChatHandler* handler) 896 | { 897 | Player* player = handler->GetSession()->GetPlayer(); 898 | ObjectGuid guid = player->GetGUID(); 899 | 900 | if (!IsInChat(guid)) 901 | { 902 | handler->PSendSysMessage("You already left the GlobalChat."); 903 | return; 904 | } 905 | 906 | playersChatData[guid].SetInChat(false); 907 | handler->PSendSysMessage("You have left the GlobalChat."); 908 | LOG_INFO("module", "GlobalChat: Player {} left GlobalChat.", player->GetName()); 909 | } 910 | 911 | void GlobalChatMgr::PlayerInfoCommand(ChatHandler* handler, Player* player) 912 | { 913 | ObjectGuid guid = player->GetGUID(); 914 | 915 | bool inChat = IsInChat(guid); 916 | time_t lastMessage = playersChatData[guid].GetLastMessage(); 917 | time_t muteTime = playersChatData[guid].GetMuteTime(); 918 | uint32 totalMutes = playersChatData[guid].GetTotalMutes(); 919 | bool isBanned = playersChatData[guid].IsBanned(); 920 | 921 | bool isMuted = muteTime > GameTime::GetGameTime().count(); 922 | std::string lastMsgStr = Acore::Time::TimeToTimestampStr(Seconds(lastMessage)); 923 | 924 | handler->PSendSysMessage("GlobalChat information about player |cff4CFF00%s|r", player->GetName().c_str()); 925 | handler->PSendSysMessage("> In Chat: %s || Last Message: %s ", inChat ? "|cff4CFF00Yes|r" : "|cffFF0000No|r", lastMessage ? ("|cff4CFF00" + lastMsgStr + "|r") : "|cffFF0000Never|r"); 926 | handler->PSendSysMessage("> Muted: %s || Mute Time: %s", isMuted ? "|cffFF0000Yes|r" : "|cff4CFF00No|r", isMuted ? ("|cffFF0000" + secsToTimeString(muteTime - GameTime::GetGameTime().count(), true) + "|r") : "|cff4CFF000s|r"); 927 | handler->PSendSysMessage("> Total Mutes: %s%u|r || Banned: %s", totalMutes > 0 ? "|cffFF0000" : "|cff4CFF00", totalMutes, isBanned ? "|cffFF0000Yes|r" : "|cff4CFF00No|r"); 928 | } 929 | -------------------------------------------------------------------------------- /src/GlobalChatMgr.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by the 6 | * Free Software Foundation; either version 3 of the License, or (at your 7 | * option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for 12 | * more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program. If not, see . 16 | */ 17 | 18 | #ifndef _GLOBALCHATMGR_H_ 19 | #define _GLOBALCHATMGR_H_ 20 | 21 | #include "Common.h" 22 | #include "Chat.h" 23 | #include "DBCFileLoader.h" 24 | #include "DBCStores.h" 25 | #include "GlobalChatData.h" 26 | #include "Player.h" 27 | #include "SocialMgr.h" 28 | #include 29 | #include 30 | 31 | class GlobalChatData; 32 | 33 | struct ChatProfanityEntry 34 | { 35 | //uint32 ID; 36 | char const* Text; 37 | int32 Language; 38 | }; 39 | 40 | enum GlobalChatAcoreStrings 41 | { 42 | LANG_FORBIDDEN_PHRASE_ANNOUNCE_GM = 17000, // Entry from sql acore_strings 43 | LANG_FORBIDDEN_URL_ANNOUNCE_GM, 44 | LANG_GLOBALCHAT_STATE_ANNOUNCE_WORLD, 45 | LANG_GLOBALCHAT_PLAYER_MUTED_ANNOUNCE_WORLD, 46 | LANG_GLOBALCHAT_PLAYER_BANNED_ANNOUNCE_WORLD, 47 | LANG_GLOBALCHAT_MUTED_ANNOUNCE_SELF, 48 | LANG_GLOBALCHAT_BANNED_ANNOUNCE_SELF, 49 | }; 50 | 51 | char constexpr ChatProfanityEntryfmt[] = "dsi"; 52 | 53 | class GlobalChatMgr 54 | { 55 | public: 56 | static GlobalChatMgr* instance(); 57 | 58 | bool GlobalChatEnabled; 59 | bool Announce; 60 | std::string ChatName; 61 | std::string ChatNameColor; 62 | std::string ChatTextColor; 63 | uint32 PlayerColor; 64 | bool FactionIcon; 65 | bool RaceIcon; 66 | bool ClassIcon; 67 | uint32 GMBadge; 68 | bool FactionSpecific; 69 | bool EnableOnLogin; 70 | uint32 MinPlayTime; 71 | uint32 CoolDown; 72 | bool SendIgnored; 73 | bool JoinChannel; 74 | bool AnnounceMutes; 75 | uint32 ProfanityBlockType; 76 | uint32 ProfanityBlockLevel; 77 | uint32 ProfanityMuteType; 78 | uint32 ProfanityMute; 79 | bool ProfanityFromDBC; 80 | uint32 URLBlockType; 81 | uint32 URLBlockLevel; 82 | uint32 URLMuteType; 83 | uint32 URLMute; 84 | 85 | const std::regex urlRegex = std::regex{ "((?:http|ftp)s?://)?([\\w]*(?::[\\w]*)?@)?((?:(www\\.)?(([a-zA-Z0-9-\\.]{1,256})(\\.[a-zA-Z]{2,63})))|((?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)))(:\\d+)?(/[\\w\\.()-/\\\\]*)?((\\?([^#\\s&]+=[^#\\s&]+)(?:&([^#\\s&]+=[^#\\s&]+))*)?(#\\S*)?)?" }; 86 | 87 | typedef std::unordered_map ProfanityRegexMap; 88 | ProfanityRegexMap ProfanityBlacklist; 89 | std::vector GMColors; 90 | std::vector URLWhitelist; 91 | 92 | void LoadConfig(bool reload); 93 | void LoadPlayerData(Player* player); 94 | void SavePlayerData(Player* player); 95 | void LoadBlacklistDB(); 96 | void LoadProfanityDBC(); 97 | 98 | bool IsInChat(ObjectGuid guid); 99 | void Mute(ObjectGuid guid, uint32 duration); 100 | void Unmute(ObjectGuid guid); 101 | void Ban(ObjectGuid guid); 102 | 103 | bool HasForbiddenPhrase(std::string message); 104 | bool HasForbiddenURL(std::string message); 105 | std::string CensorForbiddenPhrase(std::string message); 106 | std::string CensorForbiddenURL(std::string message); 107 | 108 | std::string GetFactionIcon(Player* player); 109 | std::string GetFactionColor(Player* player); 110 | std::string GetClassIcon(Player* player); 111 | std::string GetClassColor(Player* player); 112 | std::string GetRaceIcon(Player* player); 113 | 114 | void SendGlobalChat(WorldSession* session, const char* message, TeamId toTeam = TEAM_NEUTRAL); 115 | 116 | void PlayerJoinCommand(ChatHandler* handler); 117 | void PlayerLeaveCommand(ChatHandler* handler); 118 | void PlayerInfoCommand(ChatHandler* handler, Player* player); 119 | 120 | private: 121 | bool isValidHexColorCode(std::string color); 122 | std::string GetChatPrefix(); 123 | std::string GetGMChatPrefix(TeamId teamId); 124 | std::string GetNameLink(Player* player); 125 | std::string BuildChatContent(std::string text); 126 | void SendToPlayers(std::string chatMessage, Player* player, TeamId teamId); 127 | 128 | typedef std::map GlobalChatPlayersDataMap; 129 | GlobalChatPlayersDataMap playersChatData; 130 | }; 131 | 132 | #define sGlobalChatMgr GlobalChatMgr::instance() 133 | 134 | #endif 135 | -------------------------------------------------------------------------------- /src/GlobalChatScripts.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by the 6 | * Free Software Foundation; either version 3 of the License, or (at your 7 | * option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for 12 | * more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program. If not, see . 16 | */ 17 | 18 | #include "ChannelMgr.h" 19 | #include "GlobalChatMgr.h" 20 | #include "ScriptMgr.h" 21 | 22 | class GlobalChat_Config : public WorldScript 23 | { 24 | public: GlobalChat_Config() : WorldScript("GlobalChat_Config") { }; 25 | 26 | void OnAfterConfigLoad(bool reload) override 27 | { 28 | sGlobalChatMgr->LoadConfig(reload); 29 | } 30 | }; 31 | 32 | class GlobalChat_Player : public PlayerScript 33 | { 34 | public: 35 | GlobalChat_Player() : PlayerScript("GlobalChat_Player") { } 36 | 37 | void OnLogin(Player* player) 38 | { 39 | if (sGlobalChatMgr->GlobalChatEnabled) 40 | { 41 | if (sGlobalChatMgr->Announce) 42 | { 43 | ChatHandler(player->GetSession()).PSendSysMessage("This server is running the |cff4CFF00GlobalChat|r module. Use |cff4CFF00.help global|r to find out how to use it."); 44 | } 45 | 46 | sGlobalChatMgr->LoadPlayerData(player); 47 | 48 | if (!sGlobalChatMgr->IsInChat(player->GetGUID())) 49 | { 50 | if (sGlobalChatMgr->JoinChannel && !sGlobalChatMgr->ChatName.empty()) 51 | { 52 | ChatHandler(player->GetSession()).PSendSysMessage("You can join the |cffFF0000GlobalChat|r by typing |cffFF0000.joinglobal|r or |cffFF0000/join %s|r at any time.", sGlobalChatMgr->ChatName.c_str()); 53 | } 54 | else 55 | { 56 | ChatHandler(player->GetSession()).PSendSysMessage("You can join the |cffFF0000GlobalChat|r by typing |cffFF0000.joinglobal|r at any time."); 57 | } 58 | } 59 | } 60 | } 61 | 62 | void OnSave(Player* player) 63 | { 64 | sGlobalChatMgr->SavePlayerData(player); 65 | } 66 | 67 | void OnChat(Player* player, uint32 /*type*/, uint32 lang, std::string& msg, Channel* channel) 68 | { 69 | if (sGlobalChatMgr->JoinChannel && !sGlobalChatMgr->ChatName.empty() && lang != LANG_ADDON && !strcmp(channel->GetName().c_str(), sGlobalChatMgr->ChatName.c_str())) 70 | { 71 | if (sGlobalChatMgr->FactionSpecific && player->GetSession()->GetSecurity() > 0) 72 | { 73 | ChatHandler(player->GetSession()).PSendSysMessage("Please use |cff4CFF00.galliance|r or .|cff4CFF00ghorde|r for the GlobalChat as GM."); 74 | msg = -1; 75 | return; 76 | } 77 | 78 | sGlobalChatMgr->SendGlobalChat(player->GetSession(), msg.c_str()); 79 | msg = -1; 80 | } 81 | } 82 | }; 83 | 84 | void AddSC_GlobalChat() 85 | { 86 | new GlobalChat_Config(); 87 | new GlobalChat_Player(); 88 | } 89 | -------------------------------------------------------------------------------- /src/GlobalChatScripts.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by the 6 | * Free Software Foundation; either version 3 of the License, or (at your 7 | * option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for 12 | * more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program. If not, see . 16 | */ 17 | 18 | void AddSC_GlobalChat(); 19 | -------------------------------------------------------------------------------- /src/cs_globalchat.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by the 6 | * Free Software Foundation; either version 3 of the License, or (at your 7 | * option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for 12 | * more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program. If not, see . 16 | */ 17 | 18 | #include "GameTime.h" 19 | #include "ScriptMgr.h" 20 | #include "GlobalChatMgr.h" 21 | #include 22 | 23 | using namespace Acore::ChatCommands; 24 | 25 | class globalchat_commandscript : public CommandScript 26 | { 27 | public: 28 | globalchat_commandscript() : CommandScript("globalchat_commandscript") { } 29 | 30 | ChatCommandTable GetCommands() const override 31 | { 32 | static ChatCommandTable blacklistCommandTable = 33 | { 34 | { "add", HandleBlacklistAddCommand, SEC_MODERATOR, Console::Yes }, 35 | { "remove", HandleBlacklistRemoveCommand, SEC_MODERATOR, Console::Yes }, 36 | { "reload", HandleBlacklistReloadCommand, SEC_MODERATOR, Console::Yes }, 37 | }; 38 | 39 | static ChatCommandTable commandTable = 40 | { 41 | { "chat", HandleGlobalChatCommand, SEC_PLAYER, Console::Yes }, 42 | { "global", HandleGlobalChatCommand, SEC_PLAYER, Console::Yes }, 43 | { "g", HandleGlobalChatCommand, SEC_PLAYER, Console::Yes }, 44 | { "c", HandleGlobalChatCommand, SEC_PLAYER, Console::Yes }, 45 | { "joinglobal", HandleGlobalChatJoinCommand, SEC_PLAYER, Console::No }, 46 | { "leaveglobal", HandleGlobalChatLeaveCommand, SEC_PLAYER, Console::No }, 47 | { "gdisable", HandleGlobalChatDisableCommand, SEC_MODERATOR, Console::Yes }, 48 | { "genable", HandleGlobalChatEnableCommand, SEC_MODERATOR, Console::Yes }, 49 | { "gmute", HandleMuteGlobalChat, SEC_MODERATOR, Console::Yes }, 50 | { "gunmute", HandleUnmuteGlobalChat, SEC_MODERATOR, Console::Yes }, 51 | { "ginfo", HandlePlayerInfoGlobalChat, SEC_MODERATOR, Console::Yes }, 52 | { "galliance", HandleGMAllianceChatCommand, SEC_MODERATOR, Console::Yes }, 53 | { "ghorde", HandleGMHordeChatCommand, SEC_MODERATOR, Console::Yes }, 54 | { "gblacklist", blacklistCommandTable }, 55 | }; 56 | 57 | return commandTable; 58 | } 59 | 60 | static bool HandleGlobalChatCommand(ChatHandler* handler, Tail message) 61 | { 62 | if (message.empty()) 63 | return false; 64 | 65 | WorldSession* session = handler->GetSession(); 66 | 67 | if (sGlobalChatMgr->FactionSpecific && session->GetSecurity() > 0) 68 | { 69 | handler->SendSysMessage("Please use |cff4CFF00.galliance|r or .|cff4CFF00ghorde|r for the GlobalChat as GM."); 70 | handler->SetSentErrorMessage(true); 71 | return true; 72 | } 73 | 74 | sGlobalChatMgr->SendGlobalChat(session, message.data()); 75 | return true; 76 | } 77 | 78 | static bool HandleGMAllianceChatCommand(ChatHandler* handler, Tail message) 79 | { 80 | if (message.empty()) 81 | return false; 82 | 83 | WorldSession* session = handler->GetSession(); 84 | sGlobalChatMgr->SendGlobalChat(session, message.data(), TEAM_ALLIANCE); 85 | return true; 86 | } 87 | 88 | static bool HandleGMHordeChatCommand(ChatHandler* handler, Tail message) 89 | { 90 | if (message.empty()) 91 | return false; 92 | 93 | WorldSession* session = handler->GetSession(); 94 | sGlobalChatMgr->SendGlobalChat(session, message.data(), TEAM_HORDE); 95 | return true; 96 | } 97 | 98 | static bool HandleGlobalChatEnableCommand(ChatHandler* handler) 99 | { 100 | std::string playerName = "Console"; 101 | 102 | if (handler->GetSession()) 103 | { 104 | playerName = handler->GetSession()->GetPlayer()->GetName(); 105 | } 106 | 107 | if (sGlobalChatMgr->GlobalChatEnabled) 108 | { 109 | handler->SendSysMessage("The GlobalChat is already enabled."); 110 | handler->SetSentErrorMessage(true); 111 | return true; 112 | } 113 | 114 | sGlobalChatMgr->GlobalChatEnabled = true; 115 | sWorld->SendWorldText(LANG_GLOBALCHAT_STATE_ANNOUNCE_WORLD, playerName.c_str(), "enabled"); 116 | LOG_INFO("module", "GlobalChat: Player {} enabled the GlobalChat.", playerName); 117 | 118 | return true; 119 | }; 120 | 121 | static bool HandleGlobalChatDisableCommand(ChatHandler* handler) 122 | { 123 | std::string playerName = "Console"; 124 | if (handler->GetSession()) 125 | { 126 | playerName = handler->GetSession()->GetPlayer()->GetName(); 127 | } 128 | 129 | if (!sGlobalChatMgr->GlobalChatEnabled) 130 | { 131 | handler->SendSysMessage("The GlobalChat is already disabled."); 132 | handler->SetSentErrorMessage(true); 133 | return true; 134 | } 135 | 136 | sGlobalChatMgr->GlobalChatEnabled = false; 137 | sWorld->SendWorldText(LANG_GLOBALCHAT_STATE_ANNOUNCE_WORLD, playerName.c_str(), "disabled"); 138 | LOG_INFO("module", "GlobalChat: Player {} disabled the GlobalChat.", playerName); 139 | 140 | return true; 141 | }; 142 | 143 | static bool HandleGlobalChatJoinCommand(ChatHandler* handler) 144 | { 145 | sGlobalChatMgr->PlayerJoinCommand(handler); 146 | return true; 147 | }; 148 | 149 | static bool HandleGlobalChatLeaveCommand(ChatHandler* handler) 150 | { 151 | sGlobalChatMgr->PlayerLeaveCommand(handler); 152 | return true; 153 | }; 154 | 155 | static bool HandleMuteGlobalChat(ChatHandler* handler, PlayerIdentifier player, std::string duration, Tail muteReason) 156 | { 157 | std::string playerName = "Console"; 158 | Player* target = player.GetConnectedPlayer(); 159 | 160 | if (!target) 161 | { 162 | handler->SendSysMessage(LANG_PLAYER_NOT_FOUND); 163 | handler->SetSentErrorMessage(true); 164 | return false; 165 | } 166 | 167 | if (duration.empty()) 168 | return false; 169 | 170 | if (handler->GetSession() && handler->GetSession()->GetSecurity() <= target->GetSession()->GetSecurity()) 171 | { 172 | handler->SendSysMessage(LANG_YOURS_SECURITY_IS_LOW); 173 | handler->SetSentErrorMessage(true); 174 | return true; 175 | } 176 | 177 | if (handler->GetSession()) 178 | playerName = handler->GetSession()->GetPlayer()->GetName(); 179 | 180 | std::string muteReasonStr{ muteReason }; 181 | ObjectGuid guid = target->GetGUID(); 182 | 183 | if (atoi(duration.c_str()) <= 0) 184 | { 185 | sGlobalChatMgr->Ban(guid); 186 | LOG_INFO("module", "GlobalChat: Player {} banned {} in GlobalChat.", playerName, target->GetName()); 187 | 188 | if (sGlobalChatMgr->AnnounceMutes) 189 | { 190 | sWorld->SendWorldText(LANG_GLOBALCHAT_PLAYER_BANNED_ANNOUNCE_WORLD, playerName.c_str(), target->GetName().c_str(), muteReasonStr.c_str()); 191 | } 192 | else 193 | { 194 | ChatHandler(target->GetSession()).PSendSysMessage(LANG_GLOBALCHAT_BANNED_ANNOUNCE_SELF, muteReasonStr.c_str()); 195 | sWorld->SendGMText(LANG_GLOBALCHAT_PLAYER_BANNED_ANNOUNCE_WORLD, playerName.c_str(), target->GetName().c_str(), muteReasonStr.c_str()); 196 | } 197 | 198 | return true; 199 | } 200 | 201 | uint32 durationSecs = TimeStringToSecs(duration); 202 | sGlobalChatMgr->Mute(guid, durationSecs); 203 | LOG_INFO("module", "GlobalChat: Player {} muted {} for {} in GlobalChat.", playerName, target->GetName(), secsToTimeString(durationSecs, true)); 204 | 205 | if (sGlobalChatMgr->AnnounceMutes) 206 | { 207 | sWorld->SendWorldText(LANG_GLOBALCHAT_PLAYER_MUTED_ANNOUNCE_WORLD, playerName.c_str(), target->GetName().c_str(), secsToTimeString(durationSecs, true).c_str(), muteReasonStr.c_str()); 208 | } 209 | else 210 | { 211 | ChatHandler(target->GetSession()).PSendSysMessage(LANG_GLOBALCHAT_MUTED_ANNOUNCE_SELF, secsToTimeString(durationSecs, true).c_str(), muteReasonStr.c_str()); 212 | sWorld->SendGMText(LANG_GLOBALCHAT_PLAYER_MUTED_ANNOUNCE_WORLD, playerName.c_str(), target->GetName().c_str(), secsToTimeString(durationSecs, true).c_str(), muteReasonStr.c_str()); 213 | } 214 | 215 | return true; 216 | }; 217 | 218 | static bool HandleUnmuteGlobalChat(ChatHandler* handler, PlayerIdentifier player) 219 | { 220 | Player* target = player.GetConnectedPlayer(); 221 | 222 | if (!target) 223 | { 224 | handler->SendSysMessage(LANG_PLAYER_NOT_FOUND); 225 | handler->SetSentErrorMessage(true); 226 | return false; 227 | } 228 | 229 | ObjectGuid guid = target->GetGUID(); 230 | sGlobalChatMgr->Unmute(guid); 231 | LOG_INFO("module", "GlobalChat: Player {} was unmuted in GlobalChat.", target->GetName()); 232 | 233 | return true; 234 | }; 235 | 236 | static bool HandlePlayerInfoGlobalChat(ChatHandler* handler, Optional player) 237 | { 238 | if (!player) 239 | { 240 | player = PlayerIdentifier::FromTarget(handler); 241 | } 242 | 243 | if (!player || !player->IsConnected()) 244 | { 245 | handler->SendSysMessage(LANG_PLAYER_NOT_FOUND); 246 | handler->SetSentErrorMessage(true); 247 | return false; 248 | } 249 | 250 | Player* target = player->GetConnectedPlayer(); 251 | sGlobalChatMgr->PlayerInfoCommand(handler, target); 252 | 253 | return true; 254 | }; 255 | 256 | static bool HandleBlacklistAddCommand(ChatHandler* handler, Tail phrase) 257 | { 258 | if (phrase.empty()) 259 | return false; 260 | 261 | QueryResult check = CharacterDatabase.Query("SELECT * FROM `globalchat_blacklist` WHERE `phrase` = '{}'", phrase); 262 | if (check) 263 | { 264 | handler->SendSysMessage("Phrase is already blacklisted."); 265 | handler->SetSentErrorMessage(true); 266 | return true; 267 | } 268 | 269 | CharacterDatabase.Query("INSERT INTO `globalchat_blacklist` VALUES ('{}')", phrase); 270 | sGlobalChatMgr->ProfanityBlacklist[phrase.data()] = std::regex{phrase.data(), std::regex::icase | std::regex::optimize}; 271 | handler->PSendSysMessage("Phrase '%s' is now blacklisted in the GlobalChat.", phrase); 272 | LOG_INFO("module", "GlobalChat: Phrase '{}' is now blacklisted.", phrase); 273 | 274 | return true; 275 | }; 276 | 277 | static bool HandleBlacklistRemoveCommand(ChatHandler* handler, Tail phrase) 278 | { 279 | if (phrase.empty()) 280 | return false; 281 | 282 | QueryResult check = CharacterDatabase.Query("SELECT * FROM `globalchat_blacklist` WHERE `phrase` = '{}'", phrase); 283 | if (!check) 284 | { 285 | handler->SendSysMessage("Phrase is not blacklisted."); 286 | handler->SetSentErrorMessage(true); 287 | return true; 288 | } 289 | 290 | CharacterDatabase.Query("DELETE FROM `globalchat_blacklist` WHERE `phrase` = '{}'", phrase); 291 | sGlobalChatMgr->ProfanityBlacklist.erase(phrase.data()); 292 | handler->PSendSysMessage("Phrase '%s' is no longer blacklisted in the GlobalChat.", phrase); 293 | LOG_INFO("module", "GlobalChat: Phrase '{}' is no longer blacklisted.", phrase); 294 | 295 | return true; 296 | }; 297 | 298 | static bool HandleBlacklistReloadCommand(ChatHandler* handler) 299 | { 300 | sGlobalChatMgr->ProfanityBlacklist.clear(); 301 | sGlobalChatMgr->LoadBlacklistDB(); 302 | 303 | if (sGlobalChatMgr->ProfanityFromDBC) 304 | sGlobalChatMgr->LoadProfanityDBC(); 305 | 306 | handler->SendSysMessage("GlobalChat Blacklist reloaded."); 307 | LOG_INFO("module", "GlobalChat: Profanity Blacklist reloaded."); 308 | 309 | return true; 310 | }; 311 | }; 312 | 313 | void AddSC_globalchat_commandscript() 314 | { 315 | new globalchat_commandscript(); 316 | } 317 | --------------------------------------------------------------------------------