Compare commits

..

59 Commits

Author SHA1 Message Date
Jayden 1af68db747 Update README.md 2023-05-19 01:26:21 +01:00
Jayden Bailey 80f28e5920 Do not parse any Discord mentions in webhook messages
Resolves #42, closes #41
2022-04-18 21:08:32 +01:00
Jayden Bailey 27d39a9d4a Add support for the Renameuser extension 2021-11-29 19:18:49 +00:00
Jayden e5b1669d53 Merge pull request #39 from TehKittyCat/fixes
Fix Diff Links
2021-11-22 11:56:40 +00:00
TehKittyCat d3c49f1123 Fix broken diff link. 2021-11-22 02:43:27 -05:00
TehKittyCat 5ea635fd9a Remove unneeded 'use WikiPage', fixes PHP notice. 2021-11-22 01:47:51 -05:00
Jayden Bailey 4304818bdb Fix another deprecation warning 2021-11-21 18:46:34 +00:00
Jayden Bailey 3fb4b609d4 Update README to indicate new minimum MW requirement 2021-11-17 13:09:37 +00:00
Jayden Bailey 83deddd7ee Merge branch 'TehKittyCat-deprecation-fixes' 2021-11-17 13:08:40 +00:00
Jayden Bailey 160a512d67 Fix removed function call in createRevisionText (1.36+)
This commit raises the requirement of the extension to MediaWiki 1.36.
2021-11-17 13:06:16 +00:00
TehKittyCat f4dc6960bb Replace remaining usage of Revision with RevisionRecord to eliminate MediaWiki 1.36 deprecations and to fix MediaWiki 1.37 compatibility, where it has been removed. 2021-11-17 00:45:17 -05:00
TehKittyCat c24fee7a66 Replace usage of deprecated hooks. PageContentSaveComplete is replaced with PageSaveComplete and TitleMoveComplete is replaced with PageMoveComplete. This extension now requires MediaWiki 1.35 or higher. 2021-11-17 00:21:12 -05:00
TehKittyCat 1e962c55fb Fix "Title::get{Canonical,Full,Link,Local,Internal}URL method called with a second parameter is deprecated since MediaWiki 1.19. Add your parameter to an array passed as the first parameter.", fix function call casing, and use the canonical protocol. 2021-11-16 16:11:31 -05:00
Jayden Bailey 0a6992b13b Update README 2021-10-19 14:20:41 +01:00
Jayden Bailey 95567934b0 Remove composer.json. I don't use unit tests here. 2021-10-19 14:06:10 +01:00
Jayden 5e04b3afda Merge pull request #36 from dodocodes/patch-2
Update README.md
2021-09-14 02:55:52 +01:00
Jayden 91610c72fd Update README.md 2021-09-14 02:55:40 +01:00
Jayden f1cae6b097 Merge pull request #35 from DesignerThan95/patch-1
Update extension.json - added missing colon
2021-09-14 02:55:08 +01:00
DodoCodes.com 1a9cbbf48f Update README.md
Added instructions for installing missing dependencies.
2021-09-12 18:16:38 -07:00
DesignerThan df0b14017b Update extension.json - added missing colon 2021-08-17 23:38:05 +02:00
Jayden Bailey ffc78324ff Fix bug with preg_replace for username truncating and change truncation to be configurable 2021-05-04 17:27:34 +01:00
Jayden Bailey 5a303252e3 Merge branch 'master' of github.com:jaydenkieran/mw-discord 2021-05-04 17:02:24 +01:00
Jayden Bailey 4ec63619f5 Change emojis to be configurable (resolves #25) 2021-05-04 17:00:55 +01:00
Jayden Bailey 572d9e3d87 Sanitise summary/reason text input (closes #13) 2021-05-04 17:00:47 +01:00
Jayden Bailey 3eb34310ef Merge pull request #33 from Joeytje50/patch-1
Abbreviate anyone with a name longer than 25 chars
2021-05-04 16:08:34 +01:00
Joeytje50 7c20f06902 Abbreviate anyone with a name longer than 25 chars
Any username of 25 or more characters will be abbreviated to the first 20 characters, then `...`, and then the last three. This is especially helpful for RC stalkers with discord taking up only half their screen.

Different ways to abbreviate names would be possible by changing the regex pattern's numbers, or the resulting string.
2021-04-23 17:13:11 +02:00
Jayden Bailey 144499a248 Update README with branch/install info 2020-09-18 14:58:28 +01:00
Jayden Bailey 079e688ee9 Fix fatal error when blocking users
Fixes #27, closes #29
2020-09-18 14:51:07 +01:00
Jayden Bailey fb6e0f7755 Update README file 2020-06-12 16:02:35 +01:00
Jayden Bailey 4e17d17de6 Add emojis config option 2020-06-12 16:00:01 +01:00
Jayden Bailey f3b2531380 Support Approved Revs hooks
Closes #24
2020-06-12 15:36:15 +01:00
Jayden Bailey 536a4afe13 Support ArticleMergeComplete hook
Closes #16
2020-06-12 14:22:16 +01:00
Jayden Bailey b3648a462c Use RequestContext instead of wgUser
Closes #9
2019-11-23 11:48:21 +00:00
Jayden Bailey bbff717a24 Add extra debug log message 2019-11-23 09:55:16 +00:00
Jayden Bailey 6d8b2c33cb Add Content-Type header for curl requests 2019-11-23 09:53:09 +00:00
Jayden Bailey 47eef3c35d Small fix for error message 2019-11-23 09:00:24 +00:00
Jayden Bailey 7f51c4f310 Update README 2019-07-24 12:29:13 +01:00
Jayden Bailey ad101a326f Send messages for Special:Import
Fixes #6
2019-07-24 12:26:46 +01:00
Jayden Bailey 687d1abf1f Update project README file 2019-07-19 17:03:26 +01:00
Jayden Bailey 0df5463a87 Bump extension version 2019-07-19 16:59:04 +01:00
Jayden Bailey 10b4db72f7 Link directly to contribs page when user is anonymous
Resolves #12
2019-07-19 16:58:41 +01:00
Jayden Bailey fb9ada8f9c Drop cURL timeout down to 10 seconds incase of Discord downtime 2019-07-19 16:40:20 +01:00
Jayden Bailey 9a7dd03d5c Add option for using file_get_contents
Resolves #14
2019-07-19 16:39:38 +01:00
Jayden Bailey fa9149165d Bump version and update readme 2019-06-16 13:39:56 +01:00
Jayden Bailey ca3a5f8047 Add option to include timestamps on messages
Closes #11
2019-06-16 13:38:15 +01:00
Jayden Bailey 591ef8746e Merge pull request #8 from Dinoguy1000/patch-2
consistently suppress bot actions...
2019-05-13 18:33:47 +01:00
Dinoguy1000 6c2e30deab bump version number too 2019-04-30 19:28:29 -05:00
Dinoguy1000 ce780adf10 Update DiscordHooks.php 2019-04-30 19:22:35 -05:00
Jayden Bailey 680040e823 Merge pull request #3 from Railfail536/patch-1
Add Polish i18n
2019-05-01 01:20:57 +01:00
Jayden Bailey 8fa8dce03d Merge pull request #7 from Dinoguy1000/patch-1
default to revision size for diff size
2019-05-01 01:14:21 +01:00
Dinoguy1000 c3bdbecee5 consistently suppress bot actions...
...when $wgDiscordNoBots is true. This prevents (un)deletions, (un)protections, and file uploads from being reported when performed by a bot. Also did a bit of globals cleanup in one function.
2019-04-29 15:05:53 -05:00
Dinoguy1000 6ccca7b823 default to revision size for diff size
Certain events (primarily page creations) fail to display a diff size because there effectively is no diff (there is no previous revision to diff with); in these cases, just display the size of the current revision.
2019-04-29 14:52:52 -05:00
Jayden Bailey 03d612a3f7 handle potential strings in createUserLinks 2019-04-24 11:11:00 +01:00
Jayden Bailey 9830df7e97 Merge pull request #5 from xunto/xunto-patch-2
Fix unneeded broken curl_exec (Unknown Error with Visual Editor)
2019-01-29 22:16:11 +00:00
Orlov Valentine cecb90dffc Fix unneeded broken curl_exec 2019-01-30 00:44:53 +03:00
Jayden Bailey 9034275470 Update extension.json 2019-01-29 13:33:50 +00:00
Jayden Bailey 3f1e1e784b Add different message for page creations
Closes #4
2019-01-29 13:32:40 +00:00
Rail 78d66e5e31 + pl.json 2019-01-28 12:19:52 +01:00
Jayden Bailey f0cf5a741d fix substitution on remaining hooks 2019-01-24 16:51:05 +00:00
9 changed files with 576 additions and 143 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2018 Jayden Bailey
Copyright (c) 2021 Jayden Bailey
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+24 -7
View File
@@ -1,7 +1,9 @@
> ⚠️ **This branch is no longer maintained.** MediaWiki 1.36 went End-of-Life in June 2022.
# Discord (mw-discord)
MediaWiki extension for sending notifications to a Discord webhook from MediaWiki. When a certain event occurs on your MediaWiki wiki, including new edits, they can be sent as a message to a channel on a Discord server using a webhook.
Multiple webhook URLs are supported and messages will be sent to all of them using cURL, so your web server is required to have cURL installed (for most Linux distros, installing using `sudo apt install curl` should work).
Multiple webhook URLs are supported and messages will be sent to all of them.
**Live demo**: https://runescape.wiki (https://discord.gg/runescapewiki)
@@ -11,12 +13,14 @@ Multiple webhook URLs are supported and messages will be sent to all of them usi
## Requirements
- **Discord webhook URL**: This can be obtained by editing a channel on a server with the correct permissions.
- **MediaWiki 1.31+**
- **cURL**
- **MediaWiki 1.36+**. This extension aims to support the latest LTS release. **Ensure you are using the correct branch of this extension for your MediaWiki version. The `master` branch includes changes only applicable for the latest MediaWiki version.**
### Recommended
- **cURL**: By default, this extension sends requests using cURL. If you don't have cURL, you could try setting `$wgDiscordUseFileGetContents` to `true` instead, but this is not recommended.
## Installation
1. Clone this repository to your MediaWiki installation's `extensions` folder using `git clone https://github.com/jaydenkieran/mw-discord.git Discord`
1. Clone this repository to your MediaWiki installation's `extensions` folder using `git clone https://github.com/jaydenkieran/mw-discord.git -b REL1_35 Discord`
2. Modify your `LocalSettings.php` file and add:
```php
@@ -46,17 +50,22 @@ This extension can be configured using the `LocalSettings.php` file in your Medi
| `$wgDiscordNoNull` | bool | Do not send notifications for [null edits](https://www.mediawiki.org/wiki/Manual:Purge#Null_edits) | `true`
| `$wgDiscordSuppressPreviews` | bool | Force previews for links in Discord messages to be suppressed | `true`
| `$wgDiscordMaxChars` | int | Maximum amount of characters for user-generated text (e.g summaries, reasons). Set to `null` to disable truncation | `null`
| `$wgDiscordMaxCharsUsernames` | int | Maximum amount of characters for usernames. Set to `null` to disable truncation | `25`
| `$wgDiscordDisabledHooks` | array | List of hooks to disable sending webhooks for (see [below](#hooks-used)) | `[]`
| `$wgDiscordDisabledNS` | array | List of namespaces to disable sending webhooks for | `[]`
| `$wgDiscordDisabledUsers` | array | List of users whose performed actions shouldn't send webhooks | `[]`
| `$wgDiscordPrependTimestamp` | bool | Prepend a timestamp (in UTC) to all sent messages. The format can be changed by editing the MediaWiki message `discord-timestampformat` | `false`
| `$wgDiscordUseFileGetContents` | bool | Use `file_get_contents` instead of cURL. Requires `allow_url_fopen` to be set to true in `php.ini`. Not recommended as cURL makes simultaneous calls instead. | `false`
| `$wgDiscordUseEmojis` | bool | Prepend emojis to different types of messages to help distinguish them | `false`
| `$wgDiscordEmojis` | array | Map of hook names and their associated emojis to prepend to messages if `$wgDiscordUseEmojis` is enabled | See [extension.json](/extension.json)
## Hooks used
- `PageContentSaveComplete` - New edits to pages and page creations
- `PageSaveComplete` - New edits to pages and page creations
- `ArticleDeleteComplete` - Page deletions
- `ArticleUndelete` - Page restorations
- `ArticleRevisionVisibilitySet` - Revision visibility changes
- `ArticleProtectComplete` - Page protections
- `TitleMoveComplete` - Page moves
- `PageMoveComplete` - Page moves
- `LocalUserCreated` - User registrations
- `BlockIpComplete` - User blocked
- `UnblockUserComplete` - User unblocked
@@ -64,6 +73,14 @@ This extension can be configured using the `LocalSettings.php` file in your Medi
- `UploadComplete` - File was uploaded
- `FileDeleteComplete` - File revision was deleted
- `FileUndeleteComplete` - File revision was restored
- `AfterImportPage` - Page was imported
- `ArticleMergeComplete` - Article histories was merged
### [Approved Revs](https://www.mediawiki.org/wiki/Extension:Approved_Revs)
- `ApprovedRevsRevisionApproved` - Revision was approved
- `ApprovedRevsRevisionUnapproved` - Revision was unapproved
- `ApprovedRevsFileRevisionApproved` - File revision was approved
- `ApprovedRevsFileRevisionUnapproved` - File revision was unapproved
## Translation
This extension can be translated through the messages in the `ì18n` folder if you're a developer. As a wiki administrator, you may find it a better option to edit the messages on-site in the MediaWiki namespace.
@@ -71,4 +88,4 @@ This extension can be translated through the messages in the `ì18n` folder if y
Any excess whitespace in text that is translated will be stripped (e.g double spaces, etc).
## License
This extension is licensed under the MIT License, [see here](LICENSE) for more information. This project is originally inspired by Szmyk's [mediawiki-discord](https://github.com/Szmyk/mediawiki-discord) project, but has been rewritten completely to be more suitable for my needs.
This extension is licensed under the MIT License, [see here](LICENSE) for more information. This project is originally inspired by Szmyk's [mediawiki-discord](https://github.com/Szmyk/mediawiki-discord) project, but has been rewritten completely to be more suitable for my needs.
-5
View File
@@ -1,5 +0,0 @@
{
"require": {
"phpunit/phpunit": "4.8.*"
}
}
+40 -5
View File
@@ -4,9 +4,12 @@
"[https://github.com/jaydenkieran Jayden Bailey]"
],
"url": "https://github.com/jaydenkieran/mw-discord",
"version": "1.0.3",
"version": "1.0.12",
"descriptionmsg": "discord-desc",
"license-name": "MIT",
"requires": {
"MediaWiki": ">= 1.36.0"
},
"manifest_version": 1,
"config": {
"DiscordWebhookURL": [],
@@ -15,9 +18,34 @@
"DiscordNoNull": true,
"DiscordSuppressPreviews": true,
"DiscordMaxChars": null,
"DiscordMaxCharsUsernames": 25,
"DiscordDisabledHooks": [],
"DiscordDisabledNS": [],
"DiscordDisabledUsers": []
"DiscordDisabledUsers": [],
"DiscordPrependTimestamp": false,
"DiscordUseFileGetContents": false,
"DiscordUseEmojis": false,
"DiscordEmojis": {
"PageContentSaveComplete": ":pencil2:",
"ArticleDeleteComplete": ":wastebasket:",
"ArticleUndelete": ":wastebasket:",
"ArticleRevisionVisibilitySet": ":spy:",
"ArticleProtectComplete": ":lock:",
"TitleMoveComplete": ":truck:",
"LocalUserCreated": ":wave:",
"BlockIpComplete": ":no_entry_sign:",
"UnblockUserComplete": ":no_entry_sign:",
"UserGroupsChanged": ":people_holding_hands:",
"UploadComplete": ":inbox_tray:",
"FileDeleteComplete": ":wastebasket:",
"FileUndeleteComplete": ":wastebasket:",
"AfterImportPage": ":books:",
"ArticleMergeComplete": ":card_box:",
"ApprovedRevsRevisionApproved": ":white_check_mark:",
"ApprovedRevsRevisionUnapproved": ":white_check_mark:",
"ApprovedRevsFileRevisionApproved": ":white_check_mark:",
"ApprovedRevsFileRevisionUnapproved": ":white_check_mark:"
}
},
"AutoloadClasses": {
"DiscordHooks": "src/DiscordHooks.php",
@@ -27,18 +55,25 @@
"mw-discord": "i18n"
},
"Hooks": {
"PageContentSaveComplete": "DiscordHooks::onPageContentSaveComplete",
"PageSaveComplete": "DiscordHooks::onPageSaveComplete",
"ArticleDeleteComplete": "DiscordHooks::onArticleDeleteComplete",
"ArticleUndelete": "DiscordHooks::onArticleUndelete",
"ArticleRevisionVisibilitySet": "DiscordHooks::onArticleRevisionVisibilitySet",
"ArticleProtectComplete": "DiscordHooks::onArticleProtectComplete",
"TitleMoveComplete": "DiscordHooks::onTitleMoveComplete",
"PageMoveComplete": "DiscordHooks::onPageMoveComplete",
"LocalUserCreated": "DiscordHooks::onLocalUserCreated",
"BlockIpComplete": "DiscordHooks::onBlockIpComplete",
"UnblockUserComplete": "DiscordHooks::onUnblockUserComplete",
"UserGroupsChanged": "DiscordHooks::onUserGroupsChanged",
"UploadComplete": "DiscordHooks::onUploadComplete",
"FileDeleteComplete": "DiscordHooks::onFileDeleteComplete",
"FileUndeleteComplete": "DiscordHooks::onFileUndeleteComplete"
"FileUndeleteComplete": "DiscordHooks::onFileUndeleteComplete",
"AfterImportPage": "DiscordHooks::onAfterImportPage",
"ArticleMergeComplete": "DiscordHooks::onArticleMergeComplete",
"ApprovedRevsRevisionApproved": "DiscordHooks::onApprovedRevsRevisionApproved",
"ApprovedRevsRevisionUnapproved": "DiscordHooks::onApprovedRevsRevisionUnapproved",
"ApprovedRevsFileRevisionApproved": "DiscordHooks::onApprovedRevsFileRevisionApproved",
"ApprovedRevsFileRevisionUnapproved": "DiscordHooks::onApprovedRevsFileRevisionUnapproved",
"RenameUserComplete": "DiscordHooks::onRenameUserComplete"
}
}
+15 -1
View File
@@ -1,4 +1,9 @@
{
"@metadata": {
"authors": [
"Jayden Bailey"
]
},
"discord-desc": "Sends messages to a Discord channel when certain events occur",
"discord-userlinks": "$1 ($2|$3)",
"discord-revisionlinks": "($1) $2 $3",
@@ -8,6 +13,7 @@
"discord-minor": "(m)",
"discord-size": "($1)",
"discord-edit": "$1 edited $2 $3 $4",
"discord-create": "$1 created $2 $3 $4",
"discord-articledelete": "$1 deleted $2 $3 ($4 revisions deleted)",
"discord-undeleterev": "revisions for",
"discord-articleundelete": "$1 undeleted $2 $3 $4",
@@ -22,5 +28,13 @@
"discord-uploadnewver": "new version of",
"discord-uploadcomplete": "$1 uploaded $2 $3 $4 ($5, $6x$7, $8)",
"discord-filedeletecomplete": "$1 deleted a version of file $2 $3",
"discord-fileundeletecomplete": "$1 restored some versions of file $2 $3"
"discord-fileundeletecomplete": "$1 restored some versions of file $2 $3",
"discord-afterimportpage": "$1 imported $2 (imported $4/$3 revisions)",
"discord-timestampformat": "**H:i e:**",
"discord-articlemergecomplete": "$1 merged revisions from $2 into $3",
"discord-approvedrevsrevisionapproved": "$1 approved a new revision of $2 (revision $3) from user $4",
"discord-approvedrevsrevisionunapproved": "$1 unapproved the approved revision for $2",
"discord-approvedrevsfilerevisionapproved": "$1 approved a new file revision of $2 ($3) from user $4",
"discord-approvedrevsfilerevisionunapproved": "$1 unapproved the approved file revision for $2",
"discord-renameusercomplete": "$1 renamed the user $2 to $3"
}
+31
View File
@@ -0,0 +1,31 @@
{
"@metadata": {
"authors": [
"Railfail536"
]
},
"discord-desc": "Wysyła wiadomości o dokonywanych akcjach na serwer Discord",
"discord-userlinks": "$1 ($2|$3)",
"discord-revisionlinks": "($1) $2 $3",
"discord-talk": "d",
"discord-contribs": "w",
"discord-diff": "różn.",
"discord-minor": "(m)",
"discord-size": "($1)",
"discord-edit": "$1 edytował $2 $3 $4",
"discord-articledelete": "$1 usunął $2 $3 ($4 wersje usunięte)",
"discord-undeleterev": "wersje",
"discord-articleundelete": "$1 odtworzył $2 $3 $4",
"discord-revvisibility": "$1 zmienił widoczność $2 wersji $3",
"discord-articleprotect": "$1 zmienił poziom zabezpoeczenia strony $2 $2 $3 ($4)",
"discord-titlemove": "$1 przeniósł $2 na $3 $4 $5",
"discord-localusercreated": "$1 założył konto",
"discord-blocktimeformat": "d F Y H:i",
"discord-blockipcomplete": "$1 zablokował $2 $3 ($4)",
"discord-unblockusercomplete": "$1 zdjął blokadę z $2",
"discord-usergroupschanged": "$1 zmienił uprawnienia użytkownika $2 $3 ($4) ($5)",
"discord-uploadnewver": "nową wersję",
"discord-uploadcomplete": "$1 przesłał $2 $3 $4 ($5, $6x$7, $8)",
"discord-filedeletecomplete": "$1 usunął wersję pliku $2 $3",
"discord-fileundeletecomplete": "$1 odtworzył wersje pliku $2 $3"
}
+16 -2
View File
@@ -1,4 +1,9 @@
{
"@metadata": {
"authors": [
"Jayden Bailey"
]
},
"discord-desc": "The description used for the Discord extension",
"discord-userlinks": "Used for display of the user links. Parameters\n* $1 - User page link\n* $2 - discord-talk\n* $3 - discord-contribs",
"discord-revisionlinks": "Used for display of revision links. Parameters\n* $1 - discord-diff\n* $2 - discord-minor\n* $3 - discord-size",
@@ -7,7 +12,8 @@
"discord-diff": "Used as the text for $1 in discord-revisionlinks",
"discord-minor": "Used as the text for $2 in discord-revisionlinks",
"discord-size": "Used as the text for $3 in discord-revisionlinks. Parameters\n* $1 - Calculated revision size diff",
"discord-edit": "Message sent to Discord for when a page edit/creation is made. Parameters:\n* $1 - discord-userlinks\n* $2 - Page link\n* $3 - discord-revisionlinks\n* $4 - Summary (always surrounded in tildes)",
"discord-edit": "Message sent to Discord for when a page edit is made. Parameters:\n* $1 - discord-userlinks\n* $2 - Page link\n* $3 - discord-revisionlinks\n* $4 - Summary (always surrounded in tildes)",
"discord-create": "Message sent to Discord for when a page creation is made. Parameters:\n* $1 - discord-userlinks\n* $2 - Page link\n* $3 - discord-revisionlinks\n* $4 - Summary (always surrounded in tildes)",
"discord-articledelete": "Message sent to Discord when a page is deleted. Parameters:\n* $1 - discord-userlinks\n* $2 - Page link\n* $3 - Reason (always surrounded in tildes)\n* $4 - Number of revisions deleted",
"discord-undeleterev": "Used as the text for $2 in discord-articleundelete if revisions are being undeleted rather than a page",
"discord-articleundelete": "Message sent to Discord when pages/revisions are undeleted. Parameters:\n* $1 - discord-userlinks\n* $2 - discord-undeleterev\n* $3 - Page link\n* $4 - Comment (always surrounded in tildes)",
@@ -22,5 +28,13 @@
"discord-uploadnewver": "Used as the text for $2 in discord-uploadcomplete",
"discord-uploadcomplete": "Message sent to Discord when an upload is made. Parameters:\n$1 - discord-userlinks\n$2 - discord-uploadnewver\n$3 - File link\n$4 - Comment (always surrounded in tildes)\n$5 - Size (formatted as KB/MB/GB)\n$6 - File width\n$7 - File height\n$8 - File MIME type",
"discord-filedeletecomplete": "Message sent to Discord when a file version is deleted (not an entire file). Parameters:\n$1 - discord-userlinks\n$2 - File link\n$3 - Comment (always surrounded in tildes)",
"discord-fileundeletecomplete": "Message sent to Discord when a file version is restored (not an entire file). Parameters:\n$1 - discord-userlinks\n$2 - File link\n$3 - Comment (always surrounded in tildes)"
"discord-fileundeletecomplete": "Message sent to Discord when a file version is restored (not an entire file). Parameters:\n$1 - discord-userlinks\n$2 - File link\n$3 - Comment (always surrounded in tildes)",
"discord-afterimportpage": "Message sent to Discord when a page is imported. Parameters:\n$1 - discord-userlinks\n$2 - Page link\n$3 - Number of revisions in XML file\n$4 - Number of revisions successfully imported",
"discord-timestampformat": "The formatting used in the gmtime() function for displaying the time an event happened in UTC.",
"discord-articlemergecomplete": "Message sent to Discord when page histories are merged. Parameters:\n$1 - discord-userlinks\n$2 - Target link\n$3 - Destination link",
"discord-approvedrevsrevisionapproved": "Message sent to Discord when a revision is approved (Approved Revs extension). Parameters:\n$1 - discord-userlinks (admin user)\n$2 - Target link\n$3 - Revision link or ID\n$4 - discord-userlinks (revision author) or ID",
"discord-approvedrevsrevisionunapproved": "Message sent to Discord when a revision is unapproved (Approved Revs extension). Parameters:\n$1 - discord-userlinks (admin user)\n$2 - Target link",
"discord-approvedrevsfilerevisionapproved": "Message sent to Discord when a file revision is approved (Approved Revs extension). Parameters:\n$1 - discord-userlinks (admin user)\n$2 - Target link\n$3 - SHA of uploaded file\n$4 - discord-userlinks (revision author)",
"discord-approvedrevsfilerevisionunapproved": "Message sent to Discord when a file revision is unapproved (Approved Revs extension). Parameters:\n$1 - discord-userlinks (admin user)\n$2 - Target link",
"discord-renameusercomplete": "Message sent to Discord when a user is renamed (Renameuser extension). \n$1 - discord-userlinks (admin user)\n$2 - Old username\n$3 - New username link"
}
+323 -66
View File
@@ -1,4 +1,11 @@
<?php
use MediaWiki\Linker\LinkTarget;
use MediaWiki\MediaWikiServices;
use MediaWiki\Revision\RevisionRecord;
use MediaWiki\Storage\EditResult;
use MediaWiki\User\UserIdentity;
/**
* Hooks for the Discord extension
*
@@ -8,12 +15,14 @@
class DiscordHooks {
/**
* Called when a page is created or edited
* @see https://www.mediawiki.org/wiki/Manual:Hooks/PageContentSaveComplete
* @see https://www.mediawiki.org/wiki/Manual:Hooks/PageSaveComplete
*/
public static function onPageContentSaveComplete( &$wikiPage, &$user, $content, $summary, $isMinor, $isWatch, $section, &$flags, $revision, &$status, $baseRevId, $undidRevId ) {
public static function onPageSaveComplete( WikiPage $wikiPage, UserIdentity $userIdentity, string $summary, int $flags, RevisionRecord $revision, EditResult $editResult ) {
global $wgDiscordNoBots, $wgDiscordNoMinor, $wgDiscordNoNull;
$hookName = 'PageContentSaveComplete';
$user = User::newFromIdentity( $userIdentity );
if ( DiscordUtils::isDisabled( 'PageContentSaveComplete', $wikiPage->getTitle()->getNamespace(), $user ) ) {
if ( DiscordUtils::isDisabled( $hookName, $wikiPage->getTitle()->getNamespace(), $user ) ) {
return true;
}
@@ -22,26 +31,32 @@ class DiscordHooks {
return true;
}
if ( $wgDiscordNoMinor && $isMinor ) {
if ( $wgDiscordNoMinor && $revision->isMinor() ) {
// Don't continue, this is a minor edit
return true;
}
if ( $wgDiscordNoNull && ( !$revision || is_null( $status->getValue()['revision'] ) ) ) {
if ( $wgDiscordNoNull && $editResult->isNullEdit() ) {
// Don't continue, this is a null edit
return true;
}
if ( $wikiPage->getTitle()->inNamespace( NS_FILE ) && is_null( $revision->getPrevious() ) ) {
$isNew = $editResult->isNew();
if ( $wikiPage->getTitle()->inNamespace( NS_FILE ) && $isNew ) {
// Don't continue, it's a new file which onUploadComplete will handle instead
return true;
}
$msg = wfMessage( 'discord-edit', DiscordUtils::createUserLinks( $user ),
DiscordUtils::createMarkdownLink( $wikiPage->getTitle(), $wikiPage->getTitle()->getFullUrl( '', '', $proto = PROTO_HTTP ) ),
$msgKey = 'discord-edit';
if ( $isNew ) { // is a new page
$msgKey = 'discord-create';
}
$msg = wfMessage( $msgKey, DiscordUtils::createUserLinks( $user ),
DiscordUtils::createMarkdownLink( $wikiPage->getTitle(), $wikiPage->getTitle()->getFullURL( '', false, PROTO_CANONICAL ) ),
DiscordUtils::createRevisionText( $revision ),
( $summary ? ('`' . DiscordUtils::truncateText( $summary ) . '`' ) : '' ) )->plain();
DiscordUtils::handleDiscord($msg);
( $summary ? ('`' . DiscordUtils::sanitiseText( DiscordUtils::truncateText( $summary ) ) . '`' ) : '' ) )->plain();
DiscordUtils::handleDiscord($hookName, $msg);
return true;
}
@@ -50,9 +65,10 @@ class DiscordHooks {
* @see https://www.mediawiki.org/wiki/Manual:Hooks/ArticleDeleteComplete
*/
public static function onArticleDeleteComplete( &$article, User &$user, $reason, $id, $content, LogEntry $logEntry, $archivedRevisionCount ) {
global $wgDiscordNoBots, $wgDiscordNoMinor, $wgDiscordNoNull;
global $wgDiscordNoBots;
$hookName = 'ArticleDeleteComplete';
if ( DiscordUtils::isDisabled( 'ArticleDeleteComplete', $article->getTitle()->getNamespace(), $user ) ) {
if ( DiscordUtils::isDisabled( $hookName, $article->getTitle()->getNamespace(), $user ) ) {
return true;
}
@@ -62,10 +78,10 @@ class DiscordHooks {
}
$msg = wfMessage( 'discord-articledelete', DiscordUtils::createUserLinks( $user ),
DiscordUtils::createMarkdownLink( $article->getTitle(), $article->getTitle()->getFullUrl( '', '', $proto = PROTO_HTTP ) ),
( $reason ? ('`' . DiscordUtils::truncateText( $reason ) . '`' ) : '' ),
$archivedRevisionCount)->text();
DiscordUtils::handleDiscord($msg);
DiscordUtils::createMarkdownLink( $article->getTitle(), $article->getTitle()->getFullURL( '', false, PROTO_CANONICAL ) ),
( $reason ? ('`' . DiscordUtils::sanitiseText( DiscordUtils::truncateText( $reason ) ) . '`' ) : '' ),
$archivedRevisionCount)->plain();
DiscordUtils::handleDiscord($hookName, $msg);
return true;
}
@@ -74,17 +90,25 @@ class DiscordHooks {
* @see https://www.mediawiki.org/wiki/Manual:Hooks/ArticleUndelete
*/
public static function onArticleUndelete( Title $title, $create, $comment, $oldPageId, $restoredPages ) {
global $wgUser;
global $wgDiscordNoBots;
$hookName = 'ArticleUndelete';
if ( DiscordUtils::isDisabled( 'ArticleUndelete', $title->getNamespace(), $wgUser ) ) {
$user = RequestContext::getMain()->getUser();
if ( DiscordUtils::isDisabled( $hookName, $title->getNamespace(), $user ) ) {
return true;
}
$msg = wfMessage( 'discord-articleundelete', DiscordUtils::createUserLinks( $wgUser ),
if ( $wgDiscordNoBots && $user->isBot() ) {
// Don't continue, this is a bot change
return true;
}
$msg = wfMessage( 'discord-articleundelete', DiscordUtils::createUserLinks( $user ),
($create ? '' : wfMessage( 'discord-undeleterev' )->text() ),
DiscordUtils::createMarkdownLink( $title, $title->getFullUrl( '', '', $proto = PROTO_HTTP ) ),
( $comment ? ('`' . DiscordUtils::truncateText( $comment ) . '`' ) : '' ))->text();
DiscordUtils::handleDiscord($msg);
DiscordUtils::createMarkdownLink( $title, $title->getFullURL( '', false, PROTO_CANONICAL ) ),
( $comment ? ('`' . DiscordUtils::sanitiseText( DiscordUtils::truncateText( $comment ) ) . '`' ) : '' ))->plain();
DiscordUtils::handleDiscord($hookName, $msg);
return true;
}
@@ -93,16 +117,24 @@ class DiscordHooks {
* @see https://www.mediawiki.org/wiki/Manual:Hooks/ArticleRevisionVisibilitySet
*/
public static function onArticleRevisionVisibilitySet( &$title, $ids, $visibilityChangeMap ) {
global $wgUser;
global $wgDiscordNoBots;
$hookName = 'ArticleRevisionVisibilitySet';
if ( DiscordUtils::isDisabled( 'ArticleRevisionVisibilitySet', $title->getNamespace(), $wgUser ) ) {
$user = RequestContext::getMain()->getUser();
if ( DiscordUtils::isDisabled( $hookName, $title->getNamespace(), $user ) ) {
return true;
}
$msg = wfMessage( 'discord-revvisibility', DiscordUtils::createUserLinks( $wgUser ),
if ( $wgDiscordNoBots && $user->isBot() ) {
// Don't continue, this is a bot change
return true;
}
$msg = wfMessage( 'discord-revvisibility', DiscordUtils::createUserLinks( $user ),
count($visibilityChangeMap),
DiscordUtils::createMarkdownLink( $title, $title->getFullUrl( '', '', $proto = PROTO_HTTP ) ) )->text();
DiscordUtils::handleDiscord($msg);
DiscordUtils::createMarkdownLink( $title, $title->getFullURL( '', false, PROTO_CANONICAL ) ) )->plain();
DiscordUtils::handleDiscord($hookName, $msg);
return true;
}
@@ -112,8 +144,9 @@ class DiscordHooks {
*/
public static function onArticleProtectComplete( &$article, &$user, $protect, $reason ) {
global $wgDiscordNoBots;
$hookName = 'ArticleProtectComplete';
if ( DiscordUtils::isDisabled( 'ArticleProtectComplete', $article->getTitle()->getNamespace(), $user ) ) {
if ( DiscordUtils::isDisabled( $hookName, $article->getTitle()->getNamespace(), $user ) ) {
return true;
}
@@ -123,21 +156,23 @@ class DiscordHooks {
}
$msg = wfMessage( 'discord-articleprotect', DiscordUtils::createUserLinks( $user ),
DiscordUtils::createMarkdownLink( $article->getTitle(), $article->getTitle()->getFullUrl( '', '', $proto = PROTO_HTTP ) ),
( $reason ? ('`' . DiscordUtils::truncateText( $reason ) . '`' ) : '' ),
implode(", ", $protect) )->text();
DiscordUtils::handleDiscord($msg);
DiscordUtils::createMarkdownLink( $article->getTitle(), $article->getTitle()->getFullURL( '', false, PROTO_CANONICAL ) ),
( $reason ? ('`' . DiscordUtils::sanitiseText( DiscordUtils::truncateText( $reason ) ) . '`' ) : '' ),
implode(", ", $protect) )->plain();
DiscordUtils::handleDiscord($hookName, $msg);
return true;
}
/**
* Called when a page is moved
* @see https://www.mediawiki.org/wiki/Manual:Hooks/TitleMoveComplete
* @see https://www.mediawiki.org/wiki/Manual:Hooks/PageMoveComplete
*/
public static function onTitleMoveComplete( Title &$title, Title &$newTitle, User $user, $oldid, $newid, $reason, Revision $revision ) {
public static function onPageMoveComplete( LinkTarget $old, LinkTarget $new, UserIdentity $userIdentity, int $pageid, int $redirid, string $reason, RevisionRecord $revision ) {
global $wgDiscordNoBots;
$hookName = 'TitleMoveComplete';
$user = User::newFromIdentity( $userIdentity );
if ( DiscordUtils::isDisabled( 'TitleMoveComplete', $title->getNamespace(), $user ) ) {
if ( DiscordUtils::isDisabled( $hookName, $old->getNamespace(), $user ) ) {
return true;
}
@@ -147,11 +182,11 @@ class DiscordHooks {
}
$msg = wfMessage( 'discord-titlemove', DiscordUtils::createUserLinks( $user ),
DiscordUtils::createMarkdownLink( $title, $title->getFullUrl( '', '', $proto = PROTO_HTTP ) ),
DiscordUtils::createMarkdownLink( $newTitle, $newTitle->getFullUrl( '', '', $proto = PROTO_HTTP ) ),
( $reason ? ('`' . DiscordUtils::truncateText( $reason ) . '`' ) : '' ),
DiscordUtils::createRevisionText( $revision ) )->text();
DiscordUtils::handleDiscord($msg);
DiscordUtils::createMarkdownLink( $old, Title::castFromLinkTarget( $old )->getFullURL( '', false, PROTO_CANONICAL ) ),
DiscordUtils::createMarkdownLink( $new, Title::castFromLinkTarget( $new )->getFullURL( '', false, PROTO_CANONICAL ) ),
( $reason ? ('`' . DiscordUtils::sanitiseText( DiscordUtils::truncateText( $reason ) ) . '`' ) : '' ),
DiscordUtils::createRevisionText( $revision ) )->plain();
DiscordUtils::handleDiscord($hookName, $msg);
return true;
}
@@ -160,12 +195,14 @@ class DiscordHooks {
* @see https://www.mediawiki.org/wiki/Manual:Hooks/LocalUserCreated
*/
public static function onLocalUserCreated( $user, $autocreated ) {
if ( DiscordUtils::isDisabled( 'LocalUserCreated', NULL, $user ) ) {
$hookName = 'LocalUserCreated';
if ( DiscordUtils::isDisabled( $hookName, NULL, $user ) ) {
return true;
}
$msg = wfMessage( 'discord-localusercreated', DiscordUtils::createUserLinks( $user ) )->text();
DiscordUtils::handleDiscord($msg);
$msg = wfMessage( 'discord-localusercreated', DiscordUtils::createUserLinks( $user ) )->plain();
DiscordUtils::handleDiscord($hookName, $msg);
return true;
}
@@ -174,7 +211,9 @@ class DiscordHooks {
* @see https://www.mediawiki.org/wiki/Manual:Hooks/BlockIpComplete
*/
public static function onBlockIpComplete( Block $block, User $user ) {
if ( DiscordUtils::isDisabled( 'BlockIpComplete', NULL, $user ) ) {
$hookName = 'BlockIpComplete';
if ( DiscordUtils::isDisabled( $hookName, NULL, $user ) ) {
return true;
}
@@ -186,9 +225,9 @@ class DiscordHooks {
}
$msg = wfMessage( 'discord-blockipcomplete', DiscordUtils::createUserLinks( $user ), DiscordUtils::createUserLinks( $block->getTarget() ),
( $block->mReason ? ('`' . DiscordUtils::truncateText( $block->mReason ) . '`' ) : '' ),
$expiryMsg )->text();
DiscordUtils::handleDiscord($msg);
( $block->getReasonComment()->text ? ('`' . DiscordUtils::sanitiseText( DiscordUtils::truncateText( $block->getReasonComment()->text ) ) . '`' ) : '' ),
$expiryMsg )->plain();
DiscordUtils::handleDiscord($hookName, $msg);
return true;
}
@@ -197,12 +236,14 @@ class DiscordHooks {
* @see https://www.mediawiki.org/wiki/Manual:Hooks/UnblockUserComplete
*/
public static function onUnblockUserComplete( Block $block, User $user ) {
if ( DiscordUtils::isDisabled( 'UnblockUserComplete', NULL, $user ) ) {
$hookName = 'UnblockUserComplete';
if ( DiscordUtils::isDisabled( $hookName, NULL, $user ) ) {
return true;
}
$msg = wfMessage( 'discord-unblockusercomplete', DiscordUtils::createUserLinks( $user ), DiscordUtils::createUserLinks( $block->getTarget() ) )->text();
DiscordUtils::handleDiscord($msg);
DiscordUtils::handleDiscord($hookName, $msg);
return true;
}
@@ -211,7 +252,9 @@ class DiscordHooks {
* @see https://www.mediawiki.org/wiki/Manual:Hooks/UserGroupsChanged
*/
public static function onUserGroupsChanged( User $user, array $added, array $removed, $performer, $reason ) {
if ( DiscordUtils::isDisabled( 'UserGroupsChanged', NULL, $performer ) ) {
$hookName = 'UserGroupsChanged';
if ( DiscordUtils::isDisabled( $hookName, NULL, $performer ) ) {
return true;
}
@@ -222,10 +265,10 @@ class DiscordHooks {
$msg = wfMessage( 'discord-usergroupschanged', DiscordUtils::createUserLinks( $performer ),
DiscordUtils::createUserLinks( $user ),
( $reason ? ('`' . DiscordUtils::truncateText( $reason ) . '`' ) : '' ),
( $reason ? ('`' . DiscordUtils::sanitiseText( DiscordUtils::truncateText( $reason ) ) . '`' ) : '' ),
( ( count($added) > 0 ) ? ( '+ ' . join(', ', $added) ) : ''),
( ( count($removed) > 0 ) ? ( '- ' . join(', ', $removed) ) : '' ) )->text();
DiscordUtils::handleDiscord($msg);
( ( count($removed) > 0 ) ? ( '- ' . join(', ', $removed) ) : '' ) )->plain();
DiscordUtils::handleDiscord($hookName, $msg);
return true;
}
@@ -234,10 +277,18 @@ class DiscordHooks {
* @see https://www.mediawiki.org/wiki/Manual:Hooks/UploadComplete
*/
public static function onUploadComplete( &$image ) {
global $wgDiscordNoBots;
$hookName = 'UploadComplete';
$lf = $image->getLocalFile();
$user = $lf->getUser( $type = 'object' ); // only supported in MW 1.31+
if ( DiscordUtils::isDisabled( 'UploadComplete', NS_FILE, $user ) ) {
if ( DiscordUtils::isDisabled( $hookName, NS_FILE, $user ) ) {
return true;
}
if ( $wgDiscordNoBots && $user->isBot() ) {
// Don't continue, this is a bot change
return true;
}
@@ -246,13 +297,13 @@ class DiscordHooks {
$msg = wfMessage( 'discord-uploadcomplete', DiscordUtils::createUserLinks( $user ),
( $isNewRevision ? wfMessage( 'discord-uploadnewver' )->text() : '' ),
DiscordUtils::createMarkdownLink( $lf->getName(), $lf->getTitle()->getFullUrl( '', '', $proto = PROTO_HTTP ) ),
( $comment ? ('`' . DiscordUtils::truncateText( $comment ) . '`' ) : '' ),
DiscordUtils::createMarkdownLink( $lf->getName(), $lf->getTitle()->getFullURL( '', false, PROTO_CANONICAL ) ),
( $comment ? ('`' . DiscordUtils::sanitiseText( DiscordUtils::truncateText( $comment ) ) . '`' ) : '' ),
DiscordUtils::formatBytes($lf->getSize()),
$lf->getWidth(),
$lf->getHeight(),
$lf->getMimeType() )->text();
DiscordUtils::handleDiscord($msg);
$lf->getMimeType() )->plain();
DiscordUtils::handleDiscord($hookName, $msg);
return true;
}
@@ -261,7 +312,15 @@ class DiscordHooks {
* @see https://www.mediawiki.org/wiki/Manual:Hooks/FileDeleteComplete
*/
public static function onFileDeleteComplete( $file, $oldimage, $article, $user, $reason ) {
if ( DiscordUtils::isDisabled( 'FileDeleteComplete', NS_FILE, $user ) ) {
global $wgDiscordNoBots;
$hookName = 'FileDeleteComplete';
if ( DiscordUtils::isDisabled( $hookName, NS_FILE, $user ) ) {
return true;
}
if ( $wgDiscordNoBots && $user->isBot() ) {
// Don't continue, this is a bot change
return true;
}
@@ -271,9 +330,9 @@ class DiscordHooks {
}
$msg = wfMessage( 'discord-filedeletecomplete', DiscordUtils::createUserLinks( $user ),
DiscordUtils::createMarkdownLink( $file->getName(), $file->getTitle()->getFullUrl( '', '', $proto = PROTO_HTTP ) ),
( $reason ? ('`' . DiscordUtils::truncateText( $reason ) . '`' ) : '' ) )->text();
DiscordUtils::handleDiscord($msg);
DiscordUtils::createMarkdownLink( $file->getName(), $file->getTitle()->getFullURL( '', false, PROTO_CANONICAL ) ),
( $reason ? ('`' . DiscordUtils::sanitiseText( DiscordUtils::truncateText( $reason ) ) . '`' ) : '' ) )->plain();
DiscordUtils::handleDiscord($hookName, $msg);
return true;
}
@@ -282,14 +341,212 @@ class DiscordHooks {
* @see https://www.mediawiki.org/wiki/Manual:Hooks/FileUndeleteComplete
*/
public static function onFileUndeleteComplete( $title, $fileVersions, $user, $reason ) {
if ( DiscordUtils::isDisabled( 'FileUndeleteComplete', NS_FILE, $user ) ) {
global $wgDiscordNoBots;
$hookName = 'FileUndeleteComplete';
if ( DiscordUtils::isDisabled( $hookName, NS_FILE, $user ) ) {
return true;
}
if ( $wgDiscordNoBots && $user->isBot() ) {
// Don't continue, this is a bot change
return true;
}
$msg = wfMessage( 'discord-fileundeletecomplete', DiscordUtils::createUserLinks( $user ),
DiscordUtils::createMarkdownLink( $title, $title->getFullUrl( '', '', $proto = PROTO_HTTP ) ),
( $reason ? ('`' . DiscordUtils::truncateText( $reason ) . '`' ) : '' ) )->text();
DiscordUtils::handleDiscord($msg);
DiscordUtils::createMarkdownLink( $title, $title->getFullURL( '', false, PROTO_CANONICAL ) ),
( $reason ? ('`' . DiscordUtils::sanitiseText( DiscordUtils::truncateText( $reason ) ) . '`' ) : '' ) )->plain();
DiscordUtils::handleDiscord($hookName, $msg);
return true;
}
/**
* Called when a page is imported
* @see https://www.mediawiki.org/wiki/Manual:Hooks/AfterImportPage
*/
public static function onAfterImportPage( $title, $origTitle, $revCount, $sRevCount, $pageInfo ) {
global $wgDiscordNoBots;
$hookName = 'AfterImportPage';
$user = RequestContext::getMain()->getUser();
if ( DiscordUtils::isDisabled( $hookName, $title->getNamespace(), $user ) ) {
return true;
}
if ( $wgDiscordNoBots && $user->isBot() ) {
// Don't continue, this is a bot
return true;
}
$msg = wfMessage( 'discord-afterimportpage', DiscordUtils::createUserLinks( $user ),
DiscordUtils::createMarkdownLink( $title, $title->getFullURL( '', false, PROTO_CANONICAL ) ),
$revCount, $sRevCount)->plain();
DiscordUtils::handleDiscord($hookName, $msg);
return true;
}
public static function onArticleMergeComplete( $targetTitle, $destTitle ) {
global $wgDiscordNoBots;
$hookName = 'ArticleMergeComplete';
$user = RequestContext::getMain()->getUser();
if ( DiscordUtils::isDisabled( $hookName, $destTitle->getNamespace(), $user ) ) {
return true;
}
if ( $wgDiscordNoBots && $user->isBot() ) {
// Don't continue, this is a bot
return true;
}
$msg = wfMessage( 'discord-articlemergecomplete', DiscordUtils::createUserLinks( $user ),
DiscordUtils::createMarkdownLink( $targetTitle, $targetTitle->getFullURL( '', false, PROTO_CANONICAL ) ),
DiscordUtils::createMarkdownLink( $destTitle, $destTitle->getFullURL( '', false, PROTO_CANONICAL ) ))->plain();
DiscordUtils::handleDiscord($hookName, $msg);
return true;
}
/**
* Called when a revision is approved (Approved Revs extension)
* @see https://github.com/wikimedia/mediawiki-extensions-ApprovedRevs/blob/REL1_34/includes/ApprovedRevs_body.php
*/
public static function onApprovedRevsRevisionApproved ( $output, $title, $rev_id, $content ) {
global $wgDiscordNoBots;
$hookName = 'ApprovedRevsRevisionApproved';
$user = RequestContext::getMain()->getUser();
if ( DiscordUtils::isDisabled( $hookName, $title->getNamespace(), $user ) ) {
return true;
}
if ( $wgDiscordNoBots && $user->isBot() ) {
// Don't continue, this is a bot
return true;
}
// Get the revision being approved here
$rev = MediaWikiServices::getInstance()->getRevisionLookup()->getRevisionByTitle( $title, $rev_id );
$revLink = $title->getFullURL( '', false, PROTO_CANONICAL );
$revAuthor = DiscordUtils::createUserLinks( $rev->getUser( RevisionRecord::RAW ) );
$msg = wfMessage( 'discord-approvedrevsrevisionapproved', DiscordUtils::createUserLinks( $user ),
DiscordUtils::createMarkdownLink( $title, $title->getFullURL( '', false, PROTO_CANONICAL ) ),
DiscordUtils::createMarkdownLink( $rev_id, $revLink ),
$revAuthor)->plain();
DiscordUtils::handleDiscord($hookName, $msg);
return true;
}
/**
* Called when a revision is unapproved (Approved Revs extension)
* @see https://github.com/wikimedia/mediawiki-extensions-ApprovedRevs/blob/REL1_34/includes/ApprovedRevs_body.php
*/
public static function onApprovedRevsRevisionUnapproved ( $output, $title, $content ) {
global $wgDiscordNoBots;
$hookName = 'ApprovedRevsRevisionUnapproved';
$user = RequestContext::getMain()->getUser();
if ( DiscordUtils::isDisabled( $hookName, $title->getNamespace(), $user ) ) {
return true;
}
if ( $wgDiscordNoBots && $user->isBot() ) {
// Don't continue, this is a bot
return true;
}
$msg = wfMessage( 'discord-approvedrevsrevisionunapproved', DiscordUtils::createUserLinks( $user ),
DiscordUtils::createMarkdownLink( $title, $title->getFullURL( '', false, PROTO_CANONICAL ) ) )->plain();
DiscordUtils::handleDiscord($hookName, $msg);
return true;
}
/**
* Called when a file is approved (Approved Revs extension)
* @see https://github.com/wikimedia/mediawiki-extensions-ApprovedRevs/blob/REL1_34/includes/ApprovedRevs_body.php
*/
public static function onApprovedRevsFileRevisionApproved ( $parser, $title, $timestamp, $sha1 ) {
global $wgDiscordNoBots;
$hookName = 'ApprovedRevsFileRevisionApproved';
$user = RequestContext::getMain()->getUser();
if ( DiscordUtils::isDisabled( $hookName, $title->getNamespace(), $user ) ) {
return true;
}
if ( $wgDiscordNoBots && $user->isBot() ) {
// Don't continue, this is a bot
return true;
}
$imagepage = ImagePage::newFromID( $title->getArticleID() );
$displayedFile = $imagepage->getDisplayedFile();
$displayedFileUrl = $displayedFile->getCanonicalUrl(); // getFullURL doesn't work quite the same on File classes
$uploader = $displayedFile->getUser();
if (is_string($uploader)) {
$uploader = User::newFromName($uploader, false);
} else {
$uploader = User::newFromId($uploader);
}
$msg = wfMessage( 'discord-approvedrevsfilerevisionapproved', DiscordUtils::createUserLinks( $user ),
DiscordUtils::createMarkdownLink( $title, $title->getFullURL( '', false, PROTO_CANONICAL ) ),
DiscordUtils::createMarkdownLink( 'direct', $displayedFileUrl ),
DiscordUtils::createUserLinks( $uploader ) )->plain();
DiscordUtils::handleDiscord($hookName, $msg);
return true;
}
/**
* Called when a file is unapproved (Approved Revs extension)
* @see https://github.com/wikimedia/mediawiki-extensions-ApprovedRevs/blob/REL1_34/includes/ApprovedRevs_body.php
*/
public static function onApprovedRevsFileRevisionUnapproved ( $parser, $title ) {
global $wgDiscordNoBots;
$hookName = 'ApprovedRevsFileRevisionUnapproved';
$user = RequestContext::getMain()->getUser();
if ( DiscordUtils::isDisabled( $hookName, $title->getNamespace(), $user ) ) {
return true;
}
if ( $wgDiscordNoBots && $user->isBot() ) {
// Don't continue, this is a bot
return true;
}
$msg = wfMessage( 'discord-approvedrevsfilerevisionunapproved', DiscordUtils::createUserLinks( $user ),
DiscordUtils::createMarkdownLink( $title, $title->getFullURL( '', false, PROTO_CANONICAL ) ) )->plain();
DiscordUtils::handleDiscord($hookName, $msg);
return true;
}
/**
* Called when a user is renamed (Renameuser extension)
* @see https://github.com/wikimedia/mediawiki-extensions-Renameuser/blob/REL1_36/includes/RenameuserSQL.php
*/
public static function onRenameUserComplete ( $uid, $old, $new ) {
$hookName = 'RenameUserComplete';
$user = RequestContext::getMain()->getUser();
if ( DiscordUtils::isDisabled( $hookName, null, null ) ) {
return true;
}
$renamedUserAsTitle = User::newFromName( $new )->getUserPage();
$msg = wfMessage( 'discord-renameusercomplete', DiscordUtils::createUserLinks( $user ),
"*$old*",
DiscordUtils::createMarkdownLink( $new, $renamedUserAsTitle->getFullURL( '', false, PROTO_CANONICAL ) ) )->plain();
DiscordUtils::handleDiscord($hookName, $msg);
return true;
}
}
+126 -56
View File
@@ -1,5 +1,7 @@
<?php
use MediaWiki\MediaWikiServices;
class DiscordUtils {
/**
* Checks if criteria is met for this action to be cancelled
@@ -45,8 +47,8 @@ class DiscordUtils {
/**
* Handles sending a webhook to Discord using cURL
*/
public static function handleDiscord ($msg) {
global $wgDiscordWebhookURL;
public static function handleDiscord ($hookName, $msg) {
global $wgDiscordWebhookURL, $wgDiscordEmojis, $wgDiscordUseEmojis, $wgDiscordPrependTimestamp, $wgDiscordUseFileGetContents;
if ( !$wgDiscordWebhookURL ) {
// There's nothing in here, so we won't do anything
@@ -67,41 +69,81 @@ class DiscordUtils {
// Strip whitespace to just one space
$stripped = preg_replace('/\s+/', ' ', $msg);
DeferredUpdates::addCallableUpdate( function() use ( $stripped, $urls ) {
$json_data = [ 'content' => "$stripped" ];
$json = json_encode($json_data);
if ( $wgDiscordPrependTimestamp ) {
// Add timestamp
$dateString = gmdate( wfMessage( 'discord-timestampformat' )->text() );
$stripped = $dateString . ' ' . $stripped;
}
// Set up cURL multi handlers
$c_handlers = [];
$result = [];
$mh = curl_multi_init();
if ( $wgDiscordUseEmojis ) {
// Add emoji
$emoji = $wgDiscordEmojis[$hookName];
$stripped = $emoji . ' ' . $stripped;
}
foreach ($urls as &$value) {
$c_handlers[$value] = curl_init( $value );
curl_setopt( $c_handlers[$value], CURLOPT_POST, 1 ); // Send as a POST request
curl_setopt( $c_handlers[$value], CURLOPT_POSTFIELDS, $json ); // Send the JSON in the POST request
curl_setopt( $c_handlers[$value], CURLOPT_FOLLOWLOCATION, 1 );
curl_setopt( $c_handlers[$value], CURLOPT_HEADER, 0 );
curl_setopt( $c_handlers[$value], CURLOPT_RETURNTRANSFER, 1 );
curl_setopt( $c_handlers[$value], CURLOPT_CONNECTTIMEOUT, 10 ); // Add a timeout for connecting to the site
curl_setopt( $c_handlers[$value], CURLOPT_TIMEOUT, 20 ); // Do not allow cURL to run for a long time
curl_setopt( $c_handlers[$value], CURLOPT_USERAGENT, 'mw-discord/1.0 (github.com/jaydenkieran)' ); // Add a unique user agent
curl_multi_add_handle( $mh, $c_handlers[$value] );
$response = curl_exec( $ch );
DeferredUpdates::addCallableUpdate( function() use ( $stripped, $urls, $wgDiscordUseFileGetContents ) {
$user_agent = 'mw-discord/1.0 (github.com/jaydenkieran)';
$json_data = [
'content' => "$stripped",
'allowed_mentions' => [
'parse' => []
]
];
$json = json_encode($json_data);
if ( $wgDiscordUseFileGetContents ) {
// They want to use file_get_contents
foreach ($urls as &$value) {
$contextOpts = [
'http' => [
'header' => 'Content-Type: application/x-www-form-urlencoded',
'method' => 'POST', // Send as a POST request
'user_agent' => $user_agent, // Add a unique user agent
'content' => $json, // Send the JSON in the POST request
'ignore_errors' => true // If the call fails, let's not do anything with it
]
];
$context = stream_context_create( $contextOpts );
$result = file_get_contents( $value, false, $context );
}
} else {
// By default, we use cURL
// Set up cURL multi handlers
$c_handlers = [];
$result = [];
$mh = curl_multi_init();
foreach ($urls as &$value) {
$c_handlers[$value] = curl_init( $value );
curl_setopt( $c_handlers[$value], CURLOPT_POST, 1 ); // Send as a POST request
curl_setopt( $c_handlers[$value], CURLOPT_POSTFIELDS, $json ); // Send the JSON in the POST request
curl_setopt( $c_handlers[$value], CURLOPT_FOLLOWLOCATION, 1 );
curl_setopt( $c_handlers[$value], CURLOPT_HEADER, 0 );
curl_setopt( $c_handlers[$value], CURLOPT_RETURNTRANSFER, 1 );
curl_setopt( $c_handlers[$value], CURLOPT_CONNECTTIMEOUT, 10 ); // Add a timeout for connecting to the site
curl_setopt( $c_handlers[$value], CURLOPT_TIMEOUT, 10 ); // Do not allow cURL to run for a long time
curl_setopt( $c_handlers[$value], CURLOPT_USERAGENT, $user_agent ); // Add a unique user agent
curl_setopt( $c_handlers[$value], CURLOPT_HTTPHEADER, array(
'Content-Type: application/json'
));
curl_multi_add_handle( $mh, $c_handlers[$value] );
}
$running = null;
do {
curl_multi_exec($mh, $running);
} while ($running);
// Remove all handlers and then close the multi handler
foreach($c_handlers as $k => $ch) {
$result[$k] = curl_multi_getcontent($ch);
wfDebugLog( 'discord', 'Result of cURL was: ' . $result[$k] );
curl_multi_remove_handle($mh, $ch);
}
curl_multi_close($mh);
}
$running = null;
do {
curl_multi_exec($mh, $running);
} while ($running);
// Remove all handlers and then close the multi handler
foreach($c_handlers as $k => $ch) {
$result[$k] = curl_multi_getcontent($ch);
curl_multi_remove_handle($mh, $ch);
}
curl_multi_close($mh);
} );
return true;
@@ -120,10 +162,29 @@ class DiscordUtils {
* Creates links for a specific MediaWiki User object
*/
public static function createUserLinks ($user) {
$userPage = DiscordUtils::createMarkdownLink( $user, $user->getUserPage()->getFullUrl( '', '', $proto = PROTO_HTTP ) );
$userTalk = DiscordUtils::createMarkdownLink( wfMessage( 'discord-talk' )->text(), $user->getTalkPage()->getFullUrl( '', '', $proto = PROTO_HTTP ) );
$userContribs = DiscordUtils::createMarkdownLink( wfMessage( 'discord-contribs' )->text(), Title::newFromText("Special:Contributions/" . $user)->getFullURL( '', '', $proto = PROTO_HTTP ) );
$text = wfMessage( 'discord-userlinks', $userPage, $userTalk, $userContribs )->text();
global $wgDiscordMaxCharsUsernames;
if ( $user instanceof User ) {
$isAnon = $user->isAnon();
$contribs = Title::newFromText("Special:Contributions/" . $user);
if ($wgDiscordMaxCharsUsernames) {
$user_abbr = strval($user);
if (strlen($user_abbr) > $wgDiscordMaxCharsUsernames) {
$user_abbr = substr($user_abbr, 0, $wgDiscordMaxCharsUsernames);
$user_abbr = $user_abbr.'...';
}
}
$userPage = DiscordUtils::createMarkdownLink( $user_abbr, ( $isAnon ? $contribs : $user->getUserPage() )->getFullURL( '', false, PROTO_CANONICAL ) );
$userTalk = DiscordUtils::createMarkdownLink( wfMessage( 'discord-talk' )->text(), $user->getTalkPage()->getFullURL( '', false, PROTO_CANONICAL ) );
$userContribs = DiscordUtils::createMarkdownLink( wfMessage( 'discord-contribs' )->text(), $contribs->getFullURL( '', false, PROTO_CANONICAL ) );
$text = wfMessage( 'discord-userlinks', $userPage, $userTalk, $userContribs )->text();
} else {
// If it's a string, which can be likely (for example when range blocking a user)
// We need to handle this differently.
$text = wfMessage( 'discord-userlinks', $user, 'n/a', 'n/a' )->text();
}
return $text;
}
@@ -131,26 +192,26 @@ class DiscordUtils {
* Creates formatted text for a specific Revision object
*/
public static function createRevisionText ($revision) {
$diff = DiscordUtils::createMarkdownLink( wfMessage( 'discord-diff' )->text(), $revision->getTitle()->getFullUrl("diff=prev", ["oldid" => $revision->getID()], $proto = PROTO_HTTP) );
$diff = DiscordUtils::createMarkdownLink( wfMessage( 'discord-diff' )->text(), $revision->getPageAsLinkTarget()->getFullURL( [ 'diff' => 'prev', 'oldid' => $revision->getId() ], false, PROTO_CANONICAL ) );
$minor = '';
$size = '';
if ( $revision->isMinor() ) {
$minor .= wfMessage( 'discord-minor' )->text();
}
$previous = $revision->getPrevious();
if ( $previous ) {
$size .= wfMessage( 'discord-size', sprintf( "%+d", $revision->getSize() - $previous->getSize() ) )->text();
} else if ( $revision->getParentId() ) {
// Try and get the parent revision based on the ID, if we can
$previous = Revision::newFromId( $revision->getParentId() );
if ($previous) {
$size .= wfMessage( 'discord-size', sprintf( "%+d", $revision->getSize() - $previous->getSize() ) )->text();
$parentId = $revision->getParentId();
if ( $parentId ) {
$parent = MediaWikiServices::getInstance()->getRevisionLookup()->getRevisionById( $parentId );
if ( $parent ) {
$size .= wfMessage( 'discord-size', sprintf( "%+d", $revision->getSize() - $parent->getSize() ) )->text();
}
}
if ( $size == '' ) {
$size .= wfMessage( 'discord-size', sprintf( "%d", $revision->getSize() ) )->text();
}
$text = wfMessage( 'discord-revisionlinks', $diff, $minor, $size )->text();
return $text;
}
/**
* Strip bad characters from a URL
*/
@@ -160,20 +221,20 @@ class DiscordUtils {
$url = str_replace(")", "%29", $url);
return $url;
}
/**
* Formats bytes to a string representing B, KB, MB, GB, TB
*/
public static function formatBytes($bytes, $precision = 2) {
$units = array('B', 'KB', 'MB', 'GB', 'TB');
public static function formatBytes($bytes, $precision = 2) {
$units = array('B', 'KB', 'MB', 'GB', 'TB');
$bytes = max($bytes, 0);
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
$pow = min($pow, count($units) - 1);
$bytes = max($bytes, 0);
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
$pow = min($pow, count($units) - 1);
$bytes /= (1 << (10 * $pow));
$bytes /= (1 << (10 * $pow));
return round($bytes, $precision) . ' ' . $units[$pow];
return round($bytes, $precision) . ' ' . $units[$pow];
}
/**
@@ -189,6 +250,15 @@ class DiscordUtils {
}
return $text;
}
/**
* Sanitise text input, including removing the potential for abuse
* of Discord's @everyone and @here pings
*/
public static function sanitiseText($text) {
$text = preg_replace('/(`|@)/', '', $text);
return $text;
}
}
?>