]> Untitled Git - lemmy.git/commitdiff
Federated mentions. Fixes #681 (#717)
authorDessalines <dessalines@users.noreply.github.com>
Fri, 15 May 2020 16:36:11 +0000 (12:36 -0400)
committerGitHub <noreply@github.com>
Fri, 15 May 2020 16:36:11 +0000 (12:36 -0400)
* Federated mentions. Fixes #681

* Changing some todos, adding comments.

19 files changed:
server/Cargo.lock
server/Cargo.toml
server/src/api/comment.rs
server/src/api/mod.rs
server/src/apub/activities.rs
server/src/apub/comment.rs
server/src/apub/community.rs
server/src/apub/extensions/signatures.rs
server/src/apub/mod.rs
server/src/apub/post.rs
server/src/apub/shared_inbox.rs
server/src/db/community.rs
server/src/lib.rs
server/src/routes/feeds.rs
server/src/routes/mod.rs
server/src/routes/webfinger.rs
server/src/routes/websocket.rs
ui/src/api_tests/api.spec.ts
ui/src/utils.ts

index 714422b64f49d29c45da7f670478e1d8f26dc771..c71b81905eea3030d22215c8f67df42c76d919c5 100644 (file)
@@ -2,24 +2,24 @@
 # It is not intended for manual editing.
 [[package]]
 name = "activitystreams"
-version = "0.6.0"
+version = "0.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd5b29a0f2c64cc56f2b79ec29cab68a9dab3b714d811a55668d072f18a8638e"
+checksum = "edf0082191df1d6d39577b28b5daa7b8318b812038d0f7a83f05331f17580934"
 dependencies = [
  "activitystreams-derive",
  "chrono",
  "mime",
- "serde 1.0.106",
- "serde_json 1.0.51",
+ "serde 1.0.110",
+ "serde_json 1.0.53",
  "thiserror",
  "url",
 ]
 
 [[package]]
 name = "activitystreams-derive"
-version = "0.6.0"
+version = "0.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "985d3ca1ee226e83f4118e0235bc11d9fce39c4eec8d53739a21b01dd0b3f30f"
+checksum = "c39ba5929399e9f921055bac76dd8f47419fa5b6b6da1ac4c1e82b94ed0ac7b4"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -144,8 +144,8 @@ dependencies = [
  "pin-project",
  "rand 0.7.3",
  "regex 1.3.7",
- "serde 1.0.106",
- "serde_json 1.0.51",
+ "serde 1.0.110",
+ "serde_json 1.0.53",
  "serde_urlencoded",
  "sha1",
  "slab",
@@ -172,20 +172,21 @@ dependencies = [
  "http",
  "log",
  "regex 1.3.7",
- "serde 1.0.106",
+ "serde 1.0.110",
 ]
 
 [[package]]
 name = "actix-rt"
-version = "1.1.0"
+version = "1.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "20066d9200ef8d441ac156c76dd36c3f1e9a15976c34e69ae97f7f570b331882"
+checksum = "143fcc2912e0d1de2bcf4e2f720d2a60c28652ab4179685a1ee159e0fb3db227"
 dependencies = [
  "actix-macros",
  "actix-threadpool",
  "copyless",
  "futures-channel",
  "futures-util",
+ "smallvec",
  "tokio",
 ]
 
@@ -311,8 +312,8 @@ dependencies = [
  "net2",
  "pin-project",
  "regex 1.3.7",
- "serde 1.0.106",
- "serde_json 1.0.51",
+ "serde 1.0.110",
+ "serde_json 1.0.53",
  "serde_urlencoded",
  "time",
  "url",
@@ -355,6 +356,15 @@ dependencies = [
  "syn",
 ]
 
+[[package]]
+name = "addr2line"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "456d75cbb82da1ad150c8a9d97285ffcd21c9931dcb11e995903e7d75141b38b"
+dependencies = [
+ "gimli",
+]
+
 [[package]]
 name = "adler32"
 version = "1.0.4"
@@ -411,9 +421,9 @@ checksum = "71938f30533e4d95a6d17aa530939da3842c2ab6f4f84b9dae68447e4129f74a"
 
 [[package]]
 name = "async-trait"
-version = "0.1.30"
+version = "0.1.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da71fef07bc806586090247e971229289f64c210a278ee5ae419314eb386b31d"
+checksum = "26c4f3195085c36ea8d24d32b2f828d23296a9370a28aa39d111f6f16bef9f3b"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -461,33 +471,24 @@ dependencies = [
  "mime",
  "percent-encoding",
  "rand 0.7.3",
- "serde 1.0.106",
- "serde_json 1.0.51",
+ "serde 1.0.110",
+ "serde_json 1.0.53",
  "serde_urlencoded",
 ]
 
 [[package]]
 name = "backtrace"
-version = "0.3.46"
+version = "0.3.48"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1e692897359247cc6bb902933361652380af0f1b7651ae5c5013407f30e109e"
+checksum = "0df2f85c8a2abbe3b7d7e748052fdd9b76a0458fdeb16ad4223f5eca78c7c130"
 dependencies = [
- "backtrace-sys",
+ "addr2line",
  "cfg-if",
  "libc",
+ "object",
  "rustc-demangle",
 ]
 
-[[package]]
-name = "backtrace-sys"
-version = "0.1.36"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78848718ee1255a2485d1309ad9cdecfc2e7d0362dd11c6829364c6b35ae1bc7"
-dependencies = [
- "cc",
- "libc",
-]
-
 [[package]]
 name = "base64"
 version = "0.9.3"
@@ -515,21 +516,20 @@ checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7"
 
 [[package]]
 name = "base64"
-version = "0.12.0"
+version = "0.12.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d5ca2cd0adc3f48f9e9ea5a6bbdf9ccc0bfade884847e484d452414c7ccffb3"
+checksum = "53d1ccbaf7d9ec9537465a97bf19edc1a4e158ecb49fc16178202238c569cc42"
 
 [[package]]
 name = "bcrypt"
-version = "0.6.3"
+version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f055c8591efe08e03f534ee632672ea3643c3932e485f422107ec6cc1157b0ea"
+checksum = "41b70db86f3c560199b0dada79a22b9a924622384abb2a756a9707ffcce077f2"
 dependencies = [
- "base64 0.12.0",
+ "base64 0.12.1",
  "blowfish",
  "byteorder",
- "lazy_static 1.4.0",
- "rand 0.7.3",
+ "getrandom",
 ]
 
 [[package]]
@@ -607,9 +607,9 @@ checksum = "40e38929add23cdf8a366df9b0e088953150724bcbe5fc330b0d8eb3b328eec8"
 
 [[package]]
 name = "bumpalo"
-version = "3.2.1"
+version = "3.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "12ae9db68ad7fac5fe51304d20f016c911539251075a214f8e663babefa35187"
+checksum = "5356f1d23ee24a1f785a56d1d1a5f0fd5b0f6a0c0fb2412ce11da71649ab78f6"
 
 [[package]]
 name = "byte-tools"
@@ -640,9 +640,9 @@ dependencies = [
 
 [[package]]
 name = "cc"
-version = "1.0.52"
+version = "1.0.53"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3d87b23d6a92cd03af510a5ade527033f6aa6fa92161e2d5863a907d4c5e31d"
+checksum = "404b1fe4f65288577753b17e3b36a04596ee784493ec249bf81c7f2d2acd751c"
 
 [[package]]
 name = "cfg-if"
@@ -658,15 +658,15 @@ checksum = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2"
 dependencies = [
  "num-integer",
  "num-traits 0.2.11",
- "serde 1.0.106",
+ "serde 1.0.110",
  "time",
 ]
 
 [[package]]
 name = "clap"
-version = "2.33.0"
+version = "2.33.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9"
+checksum = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129"
 dependencies = [
  "ansi_term",
  "atty",
@@ -712,9 +712,9 @@ dependencies = [
  "lazy_static 1.4.0",
  "nom 5.1.1",
  "rust-ini",
- "serde 1.0.106",
+ "serde 1.0.110",
  "serde-hjson 0.9.1",
- "serde_json 1.0.51",
+ "serde_json 1.0.53",
  "toml",
  "yaml-rust",
 ]
@@ -773,9 +773,9 @@ dependencies = [
 
 [[package]]
 name = "curl"
-version = "0.4.28"
+version = "0.4.29"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eda1c0c03cacf3365d84818a40293f0e3f3953db8759c9c565a3b434edf0b52e"
+checksum = "762e34611d2d5233a506a79072be944fddd057db2f18e04c0d6fa79e3fd466fd"
 dependencies = [
  "curl-sys",
  "libc",
@@ -788,9 +788,9 @@ dependencies = [
 
 [[package]]
 name = "curl-sys"
-version = "0.4.30+curl-7.69.1"
+version = "0.4.31+curl-7.70.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "923b38e423a8f47a4058e96f2a1fa2865a6231097ee860debd678d244277d50c"
+checksum = "dcd62757cc4f5ab9404bc6ca9f0ae447e729a1403948ce5106bd588ceac6a3b0"
 dependencies = [
  "cc",
  "libc",
@@ -864,9 +864,9 @@ dependencies = [
 
 [[package]]
 name = "derive_more"
-version = "0.99.5"
+version = "0.99.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2323f3f47db9a0e77ce7a300605d8d2098597fc451ed1a97bb1f6411bb550a7"
+checksum = "46b046a346c374c6c3c84d2070bfe33904504686bdf949c2d8eb22edad3f270c"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -885,7 +885,7 @@ dependencies = [
  "diesel_derives",
  "pq-sys",
  "r2d2",
- "serde_json 1.0.51",
+ "serde_json 1.0.53",
 ]
 
 [[package]]
@@ -1035,9 +1035,9 @@ checksum = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569"
 
 [[package]]
 name = "encoding_rs"
-version = "0.8.22"
+version = "0.8.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cd8d03faa7fe0c1431609dfad7bbe827af30f82e1e2ae6f7ee4fca6bd764bc28"
+checksum = "e8ac63f94732332f44fe654443c46f6375d1939684c17b0afb6cb56b0456e171"
 dependencies = [
  "cfg-if",
 ]
@@ -1075,9 +1075,9 @@ dependencies = [
 
 [[package]]
 name = "failure"
-version = "0.1.7"
+version = "0.1.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b8529c2421efa3066a5cbd8063d2244603824daccb6936b079010bb2aa89464b"
+checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86"
 dependencies = [
  "backtrace",
  "failure_derive",
@@ -1085,9 +1085,9 @@ dependencies = [
 
 [[package]]
 name = "failure_derive"
-version = "0.1.7"
+version = "0.1.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "030a733c8287d6213886dd487564ff5c8f6aae10278b3588ed177f9d18f8d231"
+checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1124,9 +1124,9 @@ dependencies = [
 
 [[package]]
 name = "fnv"
-version = "1.0.6"
+version = "1.0.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
 
 [[package]]
 name = "foreign-types"
@@ -1167,9 +1167,9 @@ checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
 
 [[package]]
 name = "futures"
-version = "0.3.4"
+version = "0.3.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c329ae8753502fb44ae4fc2b622fa2a94652c41e795143765ba0927f92ab780"
+checksum = "1e05b85ec287aac0dc34db7d4a569323df697f9c55b99b15d6b4ef8cde49f613"
 dependencies = [
  "futures-channel",
  "futures-core",
@@ -1182,9 +1182,9 @@ dependencies = [
 
 [[package]]
 name = "futures-channel"
-version = "0.3.4"
+version = "0.3.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f0c77d04ce8edd9cb903932b608268b3fffec4163dc053b3b402bf47eac1f1a8"
+checksum = "f366ad74c28cca6ba456d95e6422883cfb4b252a83bed929c83abfdbbf2967d5"
 dependencies = [
  "futures-core",
  "futures-sink",
@@ -1192,15 +1192,15 @@ dependencies = [
 
 [[package]]
 name = "futures-core"
-version = "0.3.4"
+version = "0.3.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f25592f769825e89b92358db00d26f965761e094951ac44d3663ef25b7ac464a"
+checksum = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399"
 
 [[package]]
 name = "futures-executor"
-version = "0.3.4"
+version = "0.3.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f674f3e1bcb15b37284a90cedf55afdba482ab061c407a9c0ebbd0f3109741ba"
+checksum = "10d6bb888be1153d3abeb9006b11b02cf5e9b209fda28693c31ae1e4e012e314"
 dependencies = [
  "futures-core",
  "futures-task",
@@ -1209,15 +1209,15 @@ dependencies = [
 
 [[package]]
 name = "futures-io"
-version = "0.3.4"
+version = "0.3.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a638959aa96152c7a4cddf50fcb1e3fede0583b27157c26e67d6f99904090dc6"
+checksum = "de27142b013a8e869c14957e6d2edeef89e97c289e69d042ee3a49acd8b51789"
 
 [[package]]
 name = "futures-macro"
-version = "0.3.4"
+version = "0.3.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a5081aa3de1f7542a794a397cde100ed903b0630152d0973479018fd85423a7"
+checksum = "d0b5a30a4328ab5473878237c447333c093297bded83a4983d10f4deea240d39"
 dependencies = [
  "proc-macro-hack",
  "proc-macro2",
@@ -1227,21 +1227,24 @@ dependencies = [
 
 [[package]]
 name = "futures-sink"
-version = "0.3.4"
+version = "0.3.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3466821b4bc114d95b087b850a724c6f83115e929bc88f1fa98a3304a944c8a6"
+checksum = "3f2032893cb734c7a05d85ce0cc8b8c4075278e93b24b66f9de99d6eb0fa8acc"
 
 [[package]]
 name = "futures-task"
-version = "0.3.4"
+version = "0.3.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b0a34e53cf6cdcd0178aa573aed466b646eb3db769570841fda0c7ede375a27"
+checksum = "bdb66b5f09e22019b1ab0830f7785bcea8e7a42148683f99214f73f8ec21a626"
+dependencies = [
+ "once_cell",
+]
 
 [[package]]
 name = "futures-util"
-version = "0.3.4"
+version = "0.3.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22766cf25d64306bedf0384da004d05c9974ab104fcc4528f1236181c18004c5"
+checksum = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6"
 dependencies = [
  "futures-channel",
  "futures-core",
@@ -1250,6 +1253,7 @@ dependencies = [
  "futures-sink",
  "futures-task",
  "memchr 2.3.3",
+ "pin-project",
  "pin-utils",
  "proc-macro-hack",
  "proc-macro-nested",
@@ -1285,11 +1289,17 @@ dependencies = [
  "wasi",
 ]
 
+[[package]]
+name = "gimli"
+version = "0.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bcc8e0c9bce37868955864dbecd2b1ab2bdf967e6f28066d65aaac620444b65c"
+
 [[package]]
 name = "h2"
-version = "0.2.4"
+version = "0.2.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "377038bf3c89d18d6ca1431e7a5027194fbd724ca10592b9487ede5e8e144f42"
+checksum = "79b7246d7e4b979c03fa093da39cfb3617a96bbeee6310af63991668d7e843ff"
 dependencies = [
  "bytes",
  "fnv",
@@ -1374,9 +1384,9 @@ dependencies = [
 
 [[package]]
 name = "http-signature-normalization"
-version = "0.4.2"
+version = "0.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fde6b0321d465ea2cdc18b5d5ec73eee2ff20177ecee126cd8dd997f6c49e088"
+checksum = "648233553603e7bb55bc1ea08a514661e212c09c10f6434507894273d8b5e773"
 dependencies = [
  "chrono",
  "thiserror",
@@ -1434,9 +1444,9 @@ dependencies = [
 
 [[package]]
 name = "ipconfig"
-version = "0.2.1"
+version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa79fa216fbe60834a9c0737d7fcd30425b32d1c58854663e24d4c4b328ed83f"
+checksum = "f7e2f18aece9709094573a9f24f483c4f65caa4298e2f7ae1b71cc65d853fad7"
 dependencies = [
  "socket2",
  "widestring",
@@ -1446,9 +1456,9 @@ dependencies = [
 
 [[package]]
 name = "isahc"
-version = "0.9.1"
+version = "0.9.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50bdb3bdcbf6d534daaad1a686eda0d0dc1946818fa71e3edd3124d001adfdc2"
+checksum = "ee16417863ac2b4869ac774af7ef0322b08c53d952665edc5749ccaa37d68588"
 dependencies = [
  "bytes",
  "crossbeam-channel",
@@ -1490,9 +1500,9 @@ checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e"
 
 [[package]]
 name = "js-sys"
-version = "0.3.37"
+version = "0.3.39"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a27d435371a2fa5b6d2b028a74bbdb1234f308da363226a2854ca3ff8ba7055"
+checksum = "fa5a448de267e7358beaf4a5d849518fe9a0c13fce7afd44b06e68550e5562a7"
 dependencies = [
  "wasm-bindgen",
 ]
@@ -1506,8 +1516,8 @@ dependencies = [
  "base64 0.11.0",
  "pem",
  "ring",
- "serde 1.0.106",
- "serde_json 1.0.51",
+ "serde 1.0.110",
+ "serde_json 1.0.53",
  "simple_asn1",
 ]
 
@@ -1549,7 +1559,7 @@ dependencies = [
  "actix-rt",
  "actix-web",
  "actix-web-actors",
- "base64 0.12.0",
+ "base64 0.12.1",
  "bcrypt",
  "chrono",
  "comrak",
@@ -1576,8 +1586,8 @@ dependencies = [
  "rand 0.7.3",
  "regex 1.3.7",
  "rss",
- "serde 1.0.106",
- "serde_json 1.0.51",
+ "serde 1.0.110",
+ "serde_json 1.0.53",
  "sha2",
  "strum",
  "strum_macros",
@@ -1599,9 +1609,9 @@ dependencies = [
  "log",
  "native-tls",
  "nom 4.2.3",
- "serde 1.0.106",
+ "serde 1.0.110",
  "serde_derive",
- "serde_json 1.0.51",
+ "serde_json 1.0.53",
 ]
 
 [[package]]
@@ -1633,9 +1643,9 @@ dependencies = [
 
 [[package]]
 name = "libc"
-version = "0.2.69"
+version = "0.2.70"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005"
+checksum = "3baa92041a6fec78c687fa0cc2b3fae8884f743d672cf551bed1d6dac6988d0f"
 
 [[package]]
 name = "libnghttp2-sys"
@@ -1671,9 +1681,9 @@ dependencies = [
 
 [[package]]
 name = "linked-hash-map"
-version = "0.5.2"
+version = "0.5.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83"
+checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a"
 
 [[package]]
 name = "lock_api"
@@ -1699,7 +1709,7 @@ version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c"
 dependencies = [
- "linked-hash-map 0.5.2",
+ "linked-hash-map 0.5.3",
 ]
 
 [[package]]
@@ -1789,9 +1799,9 @@ dependencies = [
 
 [[package]]
 name = "mio"
-version = "0.6.21"
+version = "0.6.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "302dec22bcf6bae6dfb69c647187f4b4d0fb6f535521f7bc022430ce8e12008f"
+checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430"
 dependencies = [
  "cfg-if",
  "fuchsia-zircon",
@@ -1808,9 +1818,9 @@ dependencies = [
 
 [[package]]
 name = "mio-uds"
-version = "0.6.7"
+version = "0.6.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125"
+checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0"
 dependencies = [
  "iovec",
  "libc",
@@ -1849,9 +1859,9 @@ dependencies = [
 
 [[package]]
 name = "net2"
-version = "0.2.33"
+version = "0.2.34"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88"
+checksum = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7"
 dependencies = [
  "cfg-if",
  "libc",
@@ -1934,6 +1944,18 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "object"
+version = "0.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9cbca9424c482ee628fa549d9c812e2cd22f1180b9222c9200fdfa6eb31aecb2"
+
+[[package]]
+name = "once_cell"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d"
+
 [[package]]
 name = "opaque-debug"
 version = "0.2.3"
@@ -1962,9 +1984,9 @@ checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"
 
 [[package]]
 name = "openssl-sys"
-version = "0.9.55"
+version = "0.9.56"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7717097d810a0f2e2323f9e5d11e71608355e24828410b55b9d4f18aa5f9a5d8"
+checksum = "f02309a7f127000ed50594f0b50ecc69e7c654e16d41b4e8156d1b3df8e0b52e"
 dependencies = [
  "autocfg 1.0.0",
  "cc",
@@ -2059,18 +2081,18 @@ dependencies = [
 
 [[package]]
 name = "pin-project"
-version = "0.4.9"
+version = "0.4.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f6a7f5eee6292c559c793430c55c00aea9d3b3d1905e855806ca4d7253426a2"
+checksum = "81d480cb4e89522ccda96d0eed9af94180b7a5f93fb28f66e1fd7d68431663d1"
 dependencies = [
  "pin-project-internal",
 ]
 
 [[package]]
 name = "pin-project-internal"
-version = "0.4.9"
+version = "0.4.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8988430ce790d8682672117bc06dda364c0be32d3abd738234f19f3240bad99a"
+checksum = "a82996f11efccb19b685b14b5df818de31c1edcee3daa256ab5775dd98e72feb"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -2079,9 +2101,9 @@ dependencies = [
 
 [[package]]
 name = "pin-project-lite"
-version = "0.1.4"
+version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "237844750cfbb86f67afe27eee600dfbbcb6188d734139b534cbfbf4f96792ae"
+checksum = "f7505eeebd78492e0f6108f7171c4948dbb120ee8119d9d77d0afa5469bef67f"
 
 [[package]]
 name = "pin-utils"
@@ -2124,9 +2146,9 @@ checksum = "8e946095f9d3ed29ec38de908c22f95d9ac008e424c7bcae54c75a79c527c694"
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.10"
+version = "1.0.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3"
+checksum = "8872cf6f48eee44265156c111456a700ab3483686b3f96df4cf5481c89157319"
 dependencies = [
  "unicode-xid",
 ]
@@ -2149,9 +2171,9 @@ dependencies = [
 
 [[package]]
 name = "quote"
-version = "1.0.3"
+version = "1.0.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f"
+checksum = "42934bc9c8ab0d3b273a16d8551c8f0fcff46be73276ca083ec2414c15c4ba5e"
 dependencies = [
  "proc-macro2",
 ]
@@ -2400,13 +2422,13 @@ dependencies = [
 
 [[package]]
 name = "ring"
-version = "0.16.12"
+version = "0.16.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ba5a8ec64ee89a76c98c549af81ff14813df09c3e6dc4766c3856da48597a0c"
+checksum = "703516ae74571f24b465b4a1431e81e2ad51336cb0ded733a55a1aa3eccac196"
 dependencies = [
  "cc",
- "lazy_static 1.4.0",
  "libc",
+ "once_cell",
  "spin",
  "untrusted",
  "web-sys",
@@ -2464,9 +2486,9 @@ checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072"
 
 [[package]]
 name = "schannel"
-version = "0.1.18"
+version = "0.1.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "039c25b130bd8c1321ee2d7de7fde2659fa9c2744e4bb29711cfc852ea53cd19"
+checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75"
 dependencies = [
  "lazy_static 1.4.0",
  "winapi 0.3.8",
@@ -2489,9 +2511,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
 
 [[package]]
 name = "security-framework"
-version = "0.4.3"
+version = "0.4.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f331b9025654145cd425b9ded0caf8f5ae0df80d418b326e2dc1c3dc5eb0620"
+checksum = "64808902d7d99f78eaddd2b4e2509713babc3dc3c85ad6f4c447680f3c01e535"
 dependencies = [
  "bitflags",
  "core-foundation",
@@ -2533,9 +2555,9 @@ checksum = "9dad3f759919b92c3068c696c15c3d17238234498bbdcc80f2c469606f948ac8"
 
 [[package]]
 name = "serde"
-version = "1.0.106"
+version = "1.0.110"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399"
+checksum = "99e7b308464d16b56eba9964e4972a3eee817760ab60d88c3f86e1fecb08204c"
 dependencies = [
  "serde_derive",
 ]
@@ -2568,9 +2590,9 @@ dependencies = [
 
 [[package]]
 name = "serde_derive"
-version = "1.0.106"
+version = "1.0.110"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c"
+checksum = "818fbf6bfa9a42d3bfcaca148547aa00c7b915bec71d1757aa2d44ca68771984"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -2591,14 +2613,14 @@ dependencies = [
 
 [[package]]
 name = "serde_json"
-version = "1.0.51"
+version = "1.0.53"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da07b57ee2623368351e9a0488bb0b261322a15a6e0ae53e243cbdc0f4208da9"
+checksum = "993948e75b189211a9b31a7528f950c6adc21f9720b6438ff80a7fa2f864cea2"
 dependencies = [
  "indexmap",
  "itoa 0.4.5",
  "ryu",
- "serde 1.0.106",
+ "serde 1.0.110",
 ]
 
 [[package]]
@@ -2618,7 +2640,7 @@ checksum = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97"
 dependencies = [
  "dtoa 0.4.5",
  "itoa 0.4.5",
- "serde 1.0.106",
+ "serde 1.0.110",
  "url",
 ]
 
@@ -2759,9 +2781,9 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "1.0.18"
+version = "1.0.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "410a7488c0a728c7ceb4ad59b9567eb4053d02e8cc7f5c0e0eeeb39518369213"
+checksum = "4696caa4048ac7ce2bcd2e484b3cef88c1004e41b8e945a277e2c25dc0b72060"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -2814,18 +2836,18 @@ dependencies = [
 
 [[package]]
 name = "thiserror"
-version = "1.0.16"
+version = "1.0.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d12a1dae4add0f0d568eebc7bf142f145ba1aa2544cafb195c76f0f409091b60"
+checksum = "467e5ff447618a916519a4e0d62772ab14f434897f3d63f05d8700ef1e9b22c1"
 dependencies = [
  "thiserror-impl",
 ]
 
 [[package]]
 name = "thiserror-impl"
-version = "1.0.16"
+version = "1.0.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f34e0c1caaa462fd840ec6b768946ea1e7842620d94fe29d5b847138f521269"
+checksum = "e63c1091225b9834089b429bc4a2e01223470e3183e891582909e9d1c4cb55d9"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -2862,9 +2884,9 @@ dependencies = [
 
 [[package]]
 name = "threadpool"
-version = "1.8.0"
+version = "1.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e8dae184447c15d5a6916d973c642aec485105a13cd238192a6927ae3e077d66"
+checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa"
 dependencies = [
  "num_cpus",
 ]
@@ -2881,9 +2903,9 @@ dependencies = [
 
 [[package]]
 name = "tokio"
-version = "0.2.19"
+version = "0.2.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d9c43f1bb96970e153bcbae39a65e249ccb942bd9d36dbdf086024920417c9c"
+checksum = "d099fa27b9702bed751524694adbe393e18b36b204da91eb1cbbbbb4a5ee2d58"
 dependencies = [
  "bytes",
  "fnv",
@@ -2934,7 +2956,7 @@ version = "0.5.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a"
 dependencies = [
- "serde 1.0.106",
+ "serde 1.0.110",
 ]
 
 [[package]]
@@ -3063,9 +3085,9 @@ checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
 
 [[package]]
 name = "untrusted"
-version = "0.7.0"
+version = "0.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "60369ef7a31de49bcb3f6ca728d4ba7300d9a1658f94c727d4cab8c8d9f4aece"
+checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
 
 [[package]]
 name = "url"
@@ -3076,7 +3098,7 @@ dependencies = [
  "idna",
  "matches",
  "percent-encoding",
- "serde 1.0.106",
+ "serde 1.0.110",
 ]
 
 [[package]]
@@ -3101,7 +3123,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11"
 dependencies = [
  "rand 0.7.3",
- "serde 1.0.106",
+ "serde 1.0.110",
 ]
 
 [[package]]
@@ -3143,9 +3165,9 @@ checksum = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168"
 
 [[package]]
 name = "vec_map"
-version = "0.8.1"
+version = "0.8.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
+checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
 
 [[package]]
 name = "version_check"
@@ -3167,9 +3189,9 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
 
 [[package]]
 name = "wasm-bindgen"
-version = "0.2.60"
+version = "0.2.62"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2cc57ce05287f8376e998cbddfb4c8cb43b84a7ec55cf4551d7c00eef317a47f"
+checksum = "e3c7d40d09cdbf0f4895ae58cf57d92e1e57a9dd8ed2e8390514b54a47cc5551"
 dependencies = [
  "cfg-if",
  "wasm-bindgen-macro",
@@ -3177,9 +3199,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-backend"
-version = "0.2.60"
+version = "0.2.62"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d967d37bf6c16cca2973ca3af071d0a2523392e4a594548155d89a678f4237cd"
+checksum = "c3972e137ebf830900db522d6c8fd74d1900dcfc733462e9a12e942b00b4ac94"
 dependencies = [
  "bumpalo",
  "lazy_static 1.4.0",
@@ -3192,9 +3214,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro"
-version = "0.2.60"
+version = "0.2.62"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8bd151b63e1ea881bb742cd20e1d6127cef28399558f3b5d415289bc41eee3a4"
+checksum = "2cd85aa2c579e8892442954685f0d801f9129de24fa2136b2c6a539c76b65776"
 dependencies = [
  "quote",
  "wasm-bindgen-macro-support",
@@ -3202,9 +3224,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro-support"
-version = "0.2.60"
+version = "0.2.62"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d68a5b36eef1be7868f668632863292e37739656a80fc4b9acec7b0bd35a4931"
+checksum = "8eb197bd3a47553334907ffd2f16507b4f4f01bbec3ac921a7719e0decdfe72a"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -3215,15 +3237,15 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-shared"
-version = "0.2.60"
+version = "0.2.62"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "daf76fe7d25ac79748a37538b7daeed1c7a6867c92d3245c12c6222e4a20d639"
+checksum = "a91c2916119c17a8e316507afaaa2dd94b47646048014bbdf6bef098c1bb58ad"
 
 [[package]]
 name = "web-sys"
-version = "0.3.37"
+version = "0.3.39"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2d6f51648d8c56c366144378a33290049eafdd784071077f6fe37dae64c1c4cb"
+checksum = "8bc359e5dd3b46cb9687a051d50a2fdd228e4ba7cf6fcf861a5365c3d671a642"
 dependencies = [
  "js-sys",
  "wasm-bindgen",
@@ -3312,5 +3334,5 @@ version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "65923dd1784f44da1d2c3dbbc5e822045628c590ba72123e1c73d3c230c4434d"
 dependencies = [
- "linked-hash-map 0.5.2",
+ "linked-hash-map 0.5.3",
 ]
index ab76d06fa11b1f85daf0da7f7a260a3f6c9f226e..cf680b66992879af2f8ed1b8f28fafa884523018 100644 (file)
@@ -8,17 +8,17 @@ edition = "2018"
 diesel = { version = "1.4.4", features = ["postgres","chrono","r2d2","64-column-tables","serde_json"] }
 diesel_migrations = "1.4.0"
 dotenv = "0.15.0"
-activitystreams = "0.6.0"
-bcrypt = "0.6.2"
+activitystreams = "0.6.1"
+bcrypt = "0.8.0"
 chrono = { version = "0.4.7", features = ["serde"] }
-failure = "0.1.5"
+failure = "0.1.8"
 serde_json = { version = "1.0.48", features = ["preserve_order"]}
 serde = { version = "1.0.105", features = ["derive"] }
 actix = "0.9.0"
 actix-web = "2.0.0"
 actix-files = "0.2.1"
 actix-web-actors = "2.0.0"
-actix-rt = "1.0.0"
+actix-rt = "1.1.1"
 log = "0.4.0"
 env_logger = "0.7.1"
 rand = "0.7.3"
@@ -36,13 +36,13 @@ config = "0.10.1"
 hjson = "0.8.2"
 url = { version = "2.1.1", features = ["serde"] }
 percent-encoding = "2.1.0"
-isahc = "0.9"
+isahc = "0.9.2"
 comrak = "0.7"
 openssl = "0.10"
 http = "0.2.1"
-http-signature-normalization = "0.4.1"
-base64 = "0.12.0"
-tokio = "0.2.18"
-futures = "0.3.4"
+http-signature-normalization = "0.5.1"
+base64 = "0.12.1"
+tokio = "0.2.21"
+futures = "0.3.5"
 itertools = "0.9.0"
 uuid = { version = "0.8", features = ["serde", "v4"] }
index 0660a52c8598dfdc5fac6ae88899db2a04233bb1..f469b4622b5ed2aa0bc6a81c8e24fd91555a3ee2 100644 (file)
@@ -76,8 +76,6 @@ impl Perform for Oper<CreateComment> {
 
     let user_id = claims.id;
 
-    let hostname = &format!("https://{}", Settings::get().hostname);
-
     let conn = pool.get()?;
 
     // Check for a community ban
@@ -120,107 +118,9 @@ impl Perform for Oper<CreateComment> {
 
     updated_comment.send_create(&user, &conn)?;
 
-    let mut recipient_ids = Vec::new();
-
     // Scan the comment for user mentions, add those rows
-    let extracted_usernames = extract_usernames(&comment_form.content);
-
-    for username_mention in &extracted_usernames {
-      if let Ok(mention_user) = User_::read_from_name(&conn, username_mention) {
-        // You can't mention yourself
-        // At some point, make it so you can't tag the parent creator either
-        // This can cause two notifications, one for reply and the other for mention
-        if mention_user.id != user_id {
-          recipient_ids.push(mention_user.id);
-
-          let user_mention_form = UserMentionForm {
-            recipient_id: mention_user.id,
-            comment_id: inserted_comment.id,
-            read: None,
-          };
-
-          // Allow this to fail softly, since comment edits might re-update or replace it
-          // Let the uniqueness handle this fail
-          match UserMention::create(&conn, &user_mention_form) {
-            Ok(_mention) => (),
-            Err(_e) => error!("{}", &_e),
-          };
-
-          // Send an email to those users that have notifications on
-          if mention_user.send_notifications_to_email {
-            if let Some(mention_email) = mention_user.email {
-              let subject = &format!(
-                "{} - Mentioned by {}",
-                Settings::get().hostname,
-                claims.username
-              );
-              let html = &format!(
-                "<h1>User Mention</h1><br><div>{} - {}</div><br><a href={}/inbox>inbox</a>",
-                claims.username, comment_form.content, hostname
-              );
-              match send_email(subject, &mention_email, &mention_user.name, html) {
-                Ok(_o) => _o,
-                Err(e) => error!("{}", e),
-              };
-            }
-          }
-        }
-      }
-    }
-
-    // Send notifs to the parent commenter / poster
-    match data.parent_id {
-      Some(parent_id) => {
-        let parent_comment = Comment::read(&conn, parent_id)?;
-        if parent_comment.creator_id != user_id {
-          let parent_user = User_::read(&conn, parent_comment.creator_id)?;
-          recipient_ids.push(parent_user.id);
-
-          if parent_user.send_notifications_to_email {
-            if let Some(comment_reply_email) = parent_user.email {
-              let subject = &format!(
-                "{} - Reply from {}",
-                Settings::get().hostname,
-                claims.username
-              );
-              let html = &format!(
-                "<h1>Comment Reply</h1><br><div>{} - {}</div><br><a href={}/inbox>inbox</a>",
-                claims.username, comment_form.content, hostname
-              );
-              match send_email(subject, &comment_reply_email, &parent_user.name, html) {
-                Ok(_o) => _o,
-                Err(e) => error!("{}", e),
-              };
-            }
-          }
-        }
-      }
-      // Its a post
-      None => {
-        if post.creator_id != user_id {
-          let parent_user = User_::read(&conn, post.creator_id)?;
-          recipient_ids.push(parent_user.id);
-
-          if parent_user.send_notifications_to_email {
-            if let Some(post_reply_email) = parent_user.email {
-              let subject = &format!(
-                "{} - Reply from {}",
-                Settings::get().hostname,
-                claims.username
-              );
-              let html = &format!(
-                "<h1>Post Reply</h1><br><div>{} - {}</div><br><a href={}/inbox>inbox</a>",
-                claims.username, comment_form.content, hostname
-              );
-              match send_email(subject, &post_reply_email, &parent_user.name, html) {
-                Ok(_o) => _o,
-                Err(e) => error!("{}", e),
-              };
-            }
-          }
-        }
-      }
-    };
+    let mentions = scrape_text_for_mentions(&comment_form.content);
+    let recipient_ids = send_local_notifs(&conn, &mentions, &updated_comment, &user, &post);
 
     // You like your own comment by default
     let like_form = CommentLikeForm {
@@ -353,53 +253,10 @@ impl Perform for Oper<EditComment> {
       updated_comment.send_update(&user, &conn)?;
     }
 
-    let mut recipient_ids = Vec::new();
-
-    // Scan the comment for user mentions, add those rows
-    let extracted_usernames = extract_usernames(&comment_form.content);
-
-    for username_mention in &extracted_usernames {
-      let mention_user = User_::read_from_name(&conn, username_mention);
-
-      if mention_user.is_ok() {
-        let mention_user_id = mention_user?.id;
-
-        // You can't mention yourself
-        // At some point, make it so you can't tag the parent creator either
-        // This can cause two notifications, one for reply and the other for mention
-        if mention_user_id != user_id {
-          recipient_ids.push(mention_user_id);
-
-          let user_mention_form = UserMentionForm {
-            recipient_id: mention_user_id,
-            comment_id: data.edit_id,
-            read: None,
-          };
-
-          // Allow this to fail softly, since comment edits might re-update or replace it
-          // Let the uniqueness handle this fail
-          match UserMention::create(&conn, &user_mention_form) {
-            Ok(_mention) => (),
-            Err(_e) => error!("{}", &_e),
-          }
-        }
-      }
-    }
+    let post = Post::read(&conn, data.post_id)?;
 
-    // Add to recipient ids
-    match data.parent_id {
-      Some(parent_id) => {
-        let parent_comment = Comment::read(&conn, parent_id)?;
-        if parent_comment.creator_id != user_id {
-          let parent_user = User_::read(&conn, parent_comment.creator_id)?;
-          recipient_ids.push(parent_user.id);
-        }
-      }
-      None => {
-        let post = Post::read(&conn, data.post_id)?;
-        recipient_ids.push(post.creator_id);
-      }
-    }
+    let mentions = scrape_text_for_mentions(&comment_form.content);
+    let recipient_ids = send_local_notifs(&conn, &mentions, &updated_comment, &user, &post);
 
     // Mod tables
     if let Some(removed) = data.removed.to_owned() {
@@ -646,3 +503,106 @@ impl Perform for Oper<GetComments> {
     Ok(GetCommentsResponse { comments })
   }
 }
+
+pub fn send_local_notifs(
+  conn: &PgConnection,
+  mentions: &[MentionData],
+  comment: &Comment,
+  user: &User_,
+  post: &Post,
+) -> Vec<i32> {
+  let mut recipient_ids = Vec::new();
+  let hostname = &format!("https://{}", Settings::get().hostname);
+
+  // Send the local mentions
+  for mention in mentions
+    .iter()
+    .filter(|m| m.is_local() && m.name.ne(&user.name))
+    .collect::<Vec<&MentionData>>()
+  {
+    if let Ok(mention_user) = User_::read_from_name(&conn, &mention.name) {
+      // TODO
+      // At some point, make it so you can't tag the parent creator either
+      // This can cause two notifications, one for reply and the other for mention
+      recipient_ids.push(mention_user.id);
+
+      let user_mention_form = UserMentionForm {
+        recipient_id: mention_user.id,
+        comment_id: comment.id,
+        read: None,
+      };
+
+      // Allow this to fail softly, since comment edits might re-update or replace it
+      // Let the uniqueness handle this fail
+      match UserMention::create(&conn, &user_mention_form) {
+        Ok(_mention) => (),
+        Err(_e) => error!("{}", &_e),
+      };
+
+      // Send an email to those users that have notifications on
+      if mention_user.send_notifications_to_email {
+        if let Some(mention_email) = mention_user.email {
+          let subject = &format!("{} - Mentioned by {}", Settings::get().hostname, user.name,);
+          let html = &format!(
+            "<h1>User Mention</h1><br><div>{} - {}</div><br><a href={}/inbox>inbox</a>",
+            user.name, comment.content, hostname
+          );
+          match send_email(subject, &mention_email, &mention_user.name, html) {
+            Ok(_o) => _o,
+            Err(e) => error!("{}", e),
+          };
+        }
+      }
+    }
+  }
+
+  // Send notifs to the parent commenter / poster
+  match comment.parent_id {
+    Some(parent_id) => {
+      if let Ok(parent_comment) = Comment::read(&conn, parent_id) {
+        if parent_comment.creator_id != user.id {
+          if let Ok(parent_user) = User_::read(&conn, parent_comment.creator_id) {
+            recipient_ids.push(parent_user.id);
+
+            if parent_user.send_notifications_to_email {
+              if let Some(comment_reply_email) = parent_user.email {
+                let subject = &format!("{} - Reply from {}", Settings::get().hostname, user.name,);
+                let html = &format!(
+                  "<h1>Comment Reply</h1><br><div>{} - {}</div><br><a href={}/inbox>inbox</a>",
+                  user.name, comment.content, hostname
+                );
+                match send_email(subject, &comment_reply_email, &parent_user.name, html) {
+                  Ok(_o) => _o,
+                  Err(e) => error!("{}", e),
+                };
+              }
+            }
+          }
+        }
+      }
+    }
+    // Its a post
+    None => {
+      if post.creator_id != user.id {
+        if let Ok(parent_user) = User_::read(&conn, post.creator_id) {
+          recipient_ids.push(parent_user.id);
+
+          if parent_user.send_notifications_to_email {
+            if let Some(post_reply_email) = parent_user.email {
+              let subject = &format!("{} - Reply from {}", Settings::get().hostname, user.name,);
+              let html = &format!(
+                "<h1>Post Reply</h1><br><div>{} - {}</div><br><a href={}/inbox>inbox</a>",
+                user.name, comment.content, hostname
+              );
+              match send_email(subject, &post_reply_email, &parent_user.name, html) {
+                Ok(_o) => _o,
+                Err(e) => error!("{}", e),
+              };
+            }
+          }
+        }
+      }
+    }
+  };
+  recipient_ids
+}
index 9112a3be32829febd3b3888c915b48d0059d92db..fca7c035751b9f252b000a40350ed3ee193483d0 100644 (file)
@@ -18,8 +18,8 @@ use crate::db::user_mention_view::*;
 use crate::db::user_view::*;
 use crate::db::*;
 use crate::{
-  extract_usernames, fetch_iframely_and_pictshare_data, generate_random_string, naive_from_unix,
-  naive_now, remove_slurs, send_email, slur_check, slurs_vec_to_str,
+  fetch_iframely_and_pictshare_data, generate_random_string, naive_from_unix, naive_now,
+  remove_slurs, scrape_text_for_mentions, send_email, slur_check, slurs_vec_to_str, MentionData,
 };
 
 use crate::apub::{
index 23ae969872b63ebd8f00e82c809b9670ac21542c..9fc1fa1f49bfe66bc06e475924d210b10c59f544 100644 (file)
@@ -2,7 +2,7 @@ use super::*;
 
 pub fn populate_object_props(
   props: &mut ObjectProperties,
-  addressed_to: &str,
+  addressed_ccs: Vec<String>,
   object_id: &str,
 ) -> Result<(), Error> {
   props
@@ -12,7 +12,7 @@ pub fn populate_object_props(
     // TODO: should to/cc go on the Create, or on the Post? or on both?
     // TODO: handle privacy on the receiving side (at least ignore anything thats not public)
     .set_to_xsd_any_uri(public())?
-    .set_cc_xsd_any_uri(addressed_to)?;
+    .set_many_cc_xsd_any_uris(addressed_ccs)?;
   Ok(())
 }
 
index df41f67be6707f3a5d38d07fbc7c18defc7e22b3..f746fc0ff224ec5f0c733fc11f1743ab4cd8211c 100644 (file)
@@ -88,6 +88,8 @@ impl FromApub for CommentForm {
       None => None,
     };
 
+    // TODO this failed because a mention on a post that wasn't on this server yet. Has to do with
+    // fetching replytos
     let post = Post::read_from_apub_id(&conn, &post_ap_id)?;
 
     Ok(CommentForm {
@@ -121,12 +123,15 @@ impl ApubObjectType for Comment {
     let community = Community::read(conn, post.community_id)?;
     let id = format!("{}/create/{}", self.ap_id, uuid::Uuid::new_v4());
 
+    let maa: MentionsAndAddresses =
+      collect_non_local_mentions_and_addresses(&conn, &self.content, &community)?;
+
     let mut create = Create::new();
-    populate_object_props(
-      &mut create.object_props,
-      &community.get_followers_url(),
-      &id,
-    )?;
+    populate_object_props(&mut create.object_props, maa.addressed_ccs, &id)?;
+
+    // Set the mention tags
+    create.object_props.set_many_tag_base_boxes(maa.tags)?;
+
     create
       .create_props
       .set_actor_xsd_any_uri(creator.actor_id.to_owned())?
@@ -134,7 +139,7 @@ impl ApubObjectType for Comment {
 
     insert_activity(&conn, creator.id, &create, true)?;
 
-    send_activity(&create, creator, community.get_follower_inboxes(&conn)?)?;
+    send_activity(&create, creator, maa.inboxes)?;
     Ok(())
   }
 
@@ -145,12 +150,15 @@ impl ApubObjectType for Comment {
     let community = Community::read(&conn, post.community_id)?;
     let id = format!("{}/update/{}", self.ap_id, uuid::Uuid::new_v4());
 
+    let maa: MentionsAndAddresses =
+      collect_non_local_mentions_and_addresses(&conn, &self.content, &community)?;
+
     let mut update = Update::new();
-    populate_object_props(
-      &mut update.object_props,
-      &community.get_followers_url(),
-      &id,
-    )?;
+    populate_object_props(&mut update.object_props, maa.addressed_ccs, &id)?;
+
+    // Set the mention tags
+    update.object_props.set_many_tag_base_boxes(maa.tags)?;
+
     update
       .update_props
       .set_actor_xsd_any_uri(creator.actor_id.to_owned())?
@@ -158,7 +166,7 @@ impl ApubObjectType for Comment {
 
     insert_activity(&conn, creator.id, &update, true)?;
 
-    send_activity(&update, creator, community.get_follower_inboxes(&conn)?)?;
+    send_activity(&update, creator, maa.inboxes)?;
     Ok(())
   }
 
@@ -171,7 +179,7 @@ impl ApubObjectType for Comment {
 
     populate_object_props(
       &mut delete.object_props,
-      &community.get_followers_url(),
+      vec![community.get_followers_url()],
       &id,
     )?;
 
@@ -197,7 +205,7 @@ impl ApubObjectType for Comment {
 
     populate_object_props(
       &mut delete.object_props,
-      &community.get_followers_url(),
+      vec![community.get_followers_url()],
       &id,
     )?;
 
@@ -213,7 +221,7 @@ impl ApubObjectType for Comment {
 
     populate_object_props(
       &mut undo.object_props,
-      &community.get_followers_url(),
+      vec![community.get_followers_url()],
       &undo_id,
     )?;
 
@@ -237,7 +245,7 @@ impl ApubObjectType for Comment {
 
     populate_object_props(
       &mut remove.object_props,
-      &community.get_followers_url(),
+      vec![community.get_followers_url()],
       &id,
     )?;
 
@@ -263,7 +271,7 @@ impl ApubObjectType for Comment {
 
     populate_object_props(
       &mut remove.object_props,
-      &community.get_followers_url(),
+      vec![community.get_followers_url()],
       &id,
     )?;
 
@@ -278,7 +286,7 @@ impl ApubObjectType for Comment {
 
     populate_object_props(
       &mut undo.object_props,
-      &community.get_followers_url(),
+      vec![community.get_followers_url()],
       &undo_id,
     )?;
 
@@ -302,7 +310,11 @@ impl ApubLikeableType for Comment {
     let id = format!("{}/like/{}", self.ap_id, uuid::Uuid::new_v4());
 
     let mut like = Like::new();
-    populate_object_props(&mut like.object_props, &community.get_followers_url(), &id)?;
+    populate_object_props(
+      &mut like.object_props,
+      vec![community.get_followers_url()],
+      &id,
+    )?;
     like
       .like_props
       .set_actor_xsd_any_uri(creator.actor_id.to_owned())?
@@ -323,7 +335,7 @@ impl ApubLikeableType for Comment {
     let mut dislike = Dislike::new();
     populate_object_props(
       &mut dislike.object_props,
-      &community.get_followers_url(),
+      vec![community.get_followers_url()],
       &id,
     )?;
     dislike
@@ -344,7 +356,11 @@ impl ApubLikeableType for Comment {
     let id = format!("{}/dislike/{}", self.ap_id, uuid::Uuid::new_v4());
 
     let mut like = Like::new();
-    populate_object_props(&mut like.object_props, &community.get_followers_url(), &id)?;
+    populate_object_props(
+      &mut like.object_props,
+      vec![community.get_followers_url()],
+      &id,
+    )?;
     like
       .like_props
       .set_actor_xsd_any_uri(creator.actor_id.to_owned())?
@@ -357,7 +373,7 @@ impl ApubLikeableType for Comment {
 
     populate_object_props(
       &mut undo.object_props,
-      &community.get_followers_url(),
+      vec![community.get_followers_url()],
       &undo_id,
     )?;
 
@@ -372,3 +388,57 @@ impl ApubLikeableType for Comment {
     Ok(())
   }
 }
+
+struct MentionsAndAddresses {
+  addressed_ccs: Vec<String>,
+  inboxes: Vec<String>,
+  tags: Vec<Mention>,
+}
+
+/// This takes a comment, and builds a list of to_addresses, inboxes,
+/// and mention tags, so they know where to be sent to.
+/// Addresses are the users / addresses that go in the cc field.
+fn collect_non_local_mentions_and_addresses(
+  conn: &PgConnection,
+  content: &str,
+  community: &Community,
+) -> Result<MentionsAndAddresses, Error> {
+  let mut addressed_ccs = vec![community.get_followers_url()];
+
+  // Add the mention tag
+  let mut tags = Vec::new();
+
+  // Get the inboxes for any mentions
+  let mentions = scrape_text_for_mentions(&content)
+    .into_iter()
+    // Filter only the non-local ones
+    .filter(|m| !m.is_local())
+    .collect::<Vec<MentionData>>();
+  let mut mention_inboxes = Vec::new();
+  for mention in &mentions {
+    // TODO should it be fetching it every time?
+    if let Ok(actor_id) = fetch_webfinger_url(mention) {
+      debug!("mention actor_id: {}", actor_id);
+      addressed_ccs.push(actor_id.to_owned());
+      let mention_user = get_or_fetch_and_upsert_remote_user(&actor_id, &conn)?;
+      let shared_inbox = mention_user.get_shared_inbox_url();
+      mention_inboxes.push(shared_inbox);
+      let mut mention_tag = Mention::new();
+      mention_tag
+        .link_props
+        .set_href(actor_id)?
+        .set_name_xsd_string(mention.full_name())?;
+      tags.push(mention_tag);
+    }
+  }
+
+  let mut inboxes = community.get_follower_inboxes(&conn)?;
+  inboxes.extend(mention_inboxes);
+  inboxes = inboxes.into_iter().unique().collect();
+
+  Ok(MentionsAndAddresses {
+    addressed_ccs,
+    inboxes,
+    tags,
+  })
+}
index f4e8848f234c071f15dedc29409ce296961a6f20..d8ea86c7ac3651bc85bde4d26817760178def855 100644 (file)
@@ -114,7 +114,11 @@ impl ActorType for Community {
     let id = format!("{}/delete/{}", self.actor_id, uuid::Uuid::new_v4());
 
     let mut delete = Delete::default();
-    populate_object_props(&mut delete.object_props, &self.get_followers_url(), &id)?;
+    populate_object_props(
+      &mut delete.object_props,
+      vec![self.get_followers_url()],
+      &id,
+    )?;
 
     delete
       .delete_props
@@ -135,7 +139,11 @@ impl ActorType for Community {
     let id = format!("{}/delete/{}", self.actor_id, uuid::Uuid::new_v4());
 
     let mut delete = Delete::default();
-    populate_object_props(&mut delete.object_props, &self.get_followers_url(), &id)?;
+    populate_object_props(
+      &mut delete.object_props,
+      vec![self.get_followers_url()],
+      &id,
+    )?;
 
     delete
       .delete_props
@@ -147,7 +155,11 @@ impl ActorType for Community {
     let undo_id = format!("{}/undo/delete/{}", self.actor_id, uuid::Uuid::new_v4());
     let mut undo = Undo::default();
 
-    populate_object_props(&mut undo.object_props, &self.get_followers_url(), &undo_id)?;
+    populate_object_props(
+      &mut undo.object_props,
+      vec![self.get_followers_url()],
+      &undo_id,
+    )?;
 
     undo
       .undo_props
@@ -168,7 +180,11 @@ impl ActorType for Community {
     let id = format!("{}/remove/{}", self.actor_id, uuid::Uuid::new_v4());
 
     let mut remove = Remove::default();
-    populate_object_props(&mut remove.object_props, &self.get_followers_url(), &id)?;
+    populate_object_props(
+      &mut remove.object_props,
+      vec![self.get_followers_url()],
+      &id,
+    )?;
 
     remove
       .remove_props
@@ -189,7 +205,11 @@ impl ActorType for Community {
     let id = format!("{}/remove/{}", self.actor_id, uuid::Uuid::new_v4());
 
     let mut remove = Remove::default();
-    populate_object_props(&mut remove.object_props, &self.get_followers_url(), &id)?;
+    populate_object_props(
+      &mut remove.object_props,
+      vec![self.get_followers_url()],
+      &id,
+    )?;
 
     remove
       .remove_props
@@ -200,7 +220,11 @@ impl ActorType for Community {
     let undo_id = format!("{}/undo/remove/{}", self.actor_id, uuid::Uuid::new_v4());
     let mut undo = Undo::default();
 
-    populate_object_props(&mut undo.object_props, &self.get_followers_url(), &undo_id)?;
+    populate_object_props(
+      &mut undo.object_props,
+      vec![self.get_followers_url()],
+      &undo_id,
+    )?;
 
     undo
       .undo_props
index e8630f214309bb45608ac6d43235dd17e50a34bd..e79e845deeed2a8bc2eb6ab784177aa633d88de7 100644 (file)
@@ -55,7 +55,7 @@ pub fn sign(request: &Builder, actor: &dyn ActorType) -> Result<String, Error> {
         .unwrap()
         .as_str(),
       headers,
-    )
+    )?
     .sign(signing_key_id, |signing_string| {
       let private_key = PKey::private_key_from_pem(actor.private_key().as_bytes())?;
       let mut signer = Signer::new(MessageDigest::sha256(), &private_key).unwrap();
index 6ff6a0f8fac0d8c1e4ff2621bbc5b9171c4c8f86..9d4a601066d1d1f46ed0b817d70bf404f4d2f25b 100644 (file)
@@ -9,7 +9,6 @@ pub mod private_message;
 pub mod shared_inbox;
 pub mod user;
 pub mod user_inbox;
-
 use crate::api::community::CommunityResponse;
 use crate::db::activity::insert_activity;
 use crate::websocket::server::SendCommunityRoomMessage;
@@ -21,6 +20,7 @@ use activitystreams::{
   context,
   endpoint::EndpointProperties,
   ext::{Ext, Extensible},
+  link::Mention,
   object::{properties::ObjectProperties, Note, Page, Tombstone},
   public, BaseBox,
 };
@@ -38,7 +38,7 @@ use serde::{Deserialize, Serialize};
 use std::time::Duration;
 use url::Url;
 
-use crate::api::comment::CommentResponse;
+use crate::api::comment::{send_local_notifs, CommentResponse};
 use crate::api::post::PostResponse;
 use crate::api::site::SearchResponse;
 use crate::api::user::PrivateMessageResponse;
@@ -57,12 +57,13 @@ use crate::db::user::{UserForm, User_};
 use crate::db::user_view::UserView;
 use crate::db::{Crud, Followable, Joinable, Likeable, SearchType};
 use crate::routes::nodeinfo::{NodeInfo, NodeInfoWellKnown};
+use crate::routes::webfinger::WebFingerResponse;
 use crate::routes::{ChatServerParam, DbPoolParam};
 use crate::websocket::{
   server::{SendComment, SendPost, SendUserRoomMessage},
   UserOperation,
 };
-use crate::{convert_datetime, naive_now, Settings};
+use crate::{convert_datetime, naive_now, scrape_text_for_mentions, MentionData, Settings};
 
 use crate::apub::extensions::group_extensions::GroupExtension;
 use crate::apub::extensions::page_extension::PageExtension;
@@ -280,3 +281,25 @@ pub trait ActorType {
     .to_ext()
   }
 }
+
+pub fn fetch_webfinger_url(mention: &MentionData) -> Result<String, Error> {
+  let fetch_url = format!(
+    "{}://{}/.well-known/webfinger?resource=acct:{}@{}",
+    get_apub_protocol_string(),
+    mention.domain,
+    mention.name,
+    mention.domain
+  );
+  debug!("Fetching webfinger url: {}", &fetch_url);
+  let text = isahc::get(&fetch_url)?.text()?;
+  let res: WebFingerResponse = serde_json::from_str(&text)?;
+  let link = res
+    .links
+    .iter()
+    .find(|l| l.type_.eq(&Some("application/activity+json".to_string())))
+    .ok_or_else(|| format_err!("No application/activity+json link found."))?;
+  link
+    .href
+    .to_owned()
+    .ok_or_else(|| format_err!("No href found."))
+}
index b2374ffd7d585e1877518607cb0c0b641b5be9b6..3ea2040dce90688c9e09f530703c59a5bde02d02 100644 (file)
@@ -124,7 +124,7 @@ impl ApubObjectType for Post {
     let mut create = Create::new();
     populate_object_props(
       &mut create.object_props,
-      &community.get_followers_url(),
+      vec![community.get_followers_url()],
       &id,
     )?;
     create
@@ -147,7 +147,7 @@ impl ApubObjectType for Post {
     let mut update = Update::new();
     populate_object_props(
       &mut update.object_props,
-      &community.get_followers_url(),
+      vec![community.get_followers_url()],
       &id,
     )?;
     update
@@ -169,7 +169,7 @@ impl ApubObjectType for Post {
 
     populate_object_props(
       &mut delete.object_props,
-      &community.get_followers_url(),
+      vec![community.get_followers_url()],
       &id,
     )?;
 
@@ -193,7 +193,7 @@ impl ApubObjectType for Post {
 
     populate_object_props(
       &mut delete.object_props,
-      &community.get_followers_url(),
+      vec![community.get_followers_url()],
       &id,
     )?;
 
@@ -209,7 +209,7 @@ impl ApubObjectType for Post {
 
     populate_object_props(
       &mut undo.object_props,
-      &community.get_followers_url(),
+      vec![community.get_followers_url()],
       &undo_id,
     )?;
 
@@ -233,7 +233,7 @@ impl ApubObjectType for Post {
 
     populate_object_props(
       &mut remove.object_props,
-      &community.get_followers_url(),
+      vec![community.get_followers_url()],
       &id,
     )?;
 
@@ -256,7 +256,7 @@ impl ApubObjectType for Post {
 
     populate_object_props(
       &mut remove.object_props,
-      &community.get_followers_url(),
+      vec![community.get_followers_url()],
       &id,
     )?;
 
@@ -271,7 +271,7 @@ impl ApubObjectType for Post {
 
     populate_object_props(
       &mut undo.object_props,
-      &community.get_followers_url(),
+      vec![community.get_followers_url()],
       &undo_id,
     )?;
 
@@ -295,7 +295,11 @@ impl ApubLikeableType for Post {
     let id = format!("{}/like/{}", self.ap_id, uuid::Uuid::new_v4());
 
     let mut like = Like::new();
-    populate_object_props(&mut like.object_props, &community.get_followers_url(), &id)?;
+    populate_object_props(
+      &mut like.object_props,
+      vec![community.get_followers_url()],
+      &id,
+    )?;
     like
       .like_props
       .set_actor_xsd_any_uri(creator.actor_id.to_owned())?
@@ -315,7 +319,7 @@ impl ApubLikeableType for Post {
     let mut dislike = Dislike::new();
     populate_object_props(
       &mut dislike.object_props,
-      &community.get_followers_url(),
+      vec![community.get_followers_url()],
       &id,
     )?;
     dislike
@@ -335,7 +339,11 @@ impl ApubLikeableType for Post {
     let id = format!("{}/like/{}", self.ap_id, uuid::Uuid::new_v4());
 
     let mut like = Like::new();
-    populate_object_props(&mut like.object_props, &community.get_followers_url(), &id)?;
+    populate_object_props(
+      &mut like.object_props,
+      vec![community.get_followers_url()],
+      &id,
+    )?;
     like
       .like_props
       .set_actor_xsd_any_uri(creator.actor_id.to_owned())?
@@ -348,7 +356,7 @@ impl ApubLikeableType for Post {
 
     populate_object_props(
       &mut undo.object_props,
-      &community.get_followers_url(),
+      vec![community.get_followers_url()],
       &undo_id,
     )?;
 
index 6bbe9725138860decb5116aace3a8e6c7fab3b7f..b65f3a8a305d3f567d1ca2e397371d555dcc4c43 100644 (file)
@@ -167,12 +167,18 @@ fn receive_create_comment(
 
   let comment = CommentForm::from_apub(&note, &conn)?;
   let inserted_comment = Comment::create(conn, &comment)?;
+  let post = Post::read(&conn, inserted_comment.post_id)?;
+
+  // Note:
+  // Although mentions could be gotten from the post tags (they are included there), or the ccs,
+  // Its much easier to scrape them from the comment body, since the API has to do that
+  // anyway.
+  let mentions = scrape_text_for_mentions(&inserted_comment.content);
+  let recipient_ids = send_local_notifs(&conn, &mentions, &inserted_comment, &user, &post);
 
   // Refetch the view
   let comment_view = CommentView::read(&conn, inserted_comment.id, None)?;
 
-  // TODO get those recipient actor ids from somewhere
-  let recipient_ids = vec![];
   let res = CommentResponse {
     comment: comment_view,
     recipient_ids,
@@ -353,13 +359,15 @@ fn receive_update_comment(
 
   let comment = CommentForm::from_apub(&note, &conn)?;
   let comment_id = Comment::read_from_apub_id(conn, &comment.ap_id)?.id;
-  Comment::update(conn, comment_id, &comment)?;
+  let updated_comment = Comment::update(conn, comment_id, &comment)?;
+  let post = Post::read(&conn, updated_comment.post_id)?;
+
+  let mentions = scrape_text_for_mentions(&updated_comment.content);
+  let recipient_ids = send_local_notifs(&conn, &mentions, &updated_comment, &user, &post);
 
   // Refetch the view
   let comment_view = CommentView::read(&conn, comment_id, None)?;
 
-  // TODO get those recipient actor ids from somewhere
-  let recipient_ids = vec![];
   let res = CommentResponse {
     comment: comment_view,
     recipient_ids,
index 9b858fde349232ff87b966d1231ceb099025f2a0..fad19c4c5bbc073569c581518edc98037c66c30f 100644 (file)
@@ -92,10 +92,6 @@ impl Community {
     use crate::schema::community::dsl::*;
     community.filter(local.eq(true)).load::<Community>(conn)
   }
-
-  pub fn get_url(&self) -> String {
-    format!("https://{}/c/{}", Settings::get().hostname, self.name)
-  }
 }
 
 #[derive(Identifiable, Queryable, Associations, PartialEq, Debug)]
index ce886754ef32bf210f44bc2ed0683d113e61479a..b68e2af581fbaf7650bc635f6c4c686a1a477eaf 100644 (file)
@@ -40,6 +40,7 @@ use crate::settings::Settings;
 use actix_web::dev::ConnectionInfo;
 use chrono::{DateTime, FixedOffset, Local, NaiveDateTime, Utc};
 use isahc::prelude::*;
+use itertools::Itertools;
 use lettre::smtp::authentication::{Credentials, Mechanism};
 use lettre::smtp::extension::ClientId;
 use lettre::smtp::ConnectionReuseParameters;
@@ -103,20 +104,6 @@ pub fn slurs_vec_to_str(slurs: Vec<&str>) -> String {
   [start, combined].concat()
 }
 
-pub fn extract_usernames(test: &str) -> Vec<&str> {
-  let mut matches: Vec<&str> = USERNAME_MATCHES_REGEX
-    .find_iter(test)
-    .map(|mat| mat.as_str())
-    .collect();
-
-  // Unique
-  matches.sort_unstable();
-  matches.dedup();
-
-  // Remove /u/
-  matches.iter().map(|t| &t[3..]).collect()
-}
-
 pub fn generate_random_string() -> String {
   thread_rng().sample_iter(&Alphanumeric).take(30).collect()
 }
@@ -249,9 +236,48 @@ pub fn get_ip(conn_info: &ConnectionInfo) -> String {
     .to_string()
 }
 
+// TODO nothing is done with community / group webfingers yet, so just ignore those for now
+#[derive(Clone, PartialEq, Eq, Hash)]
+pub struct MentionData {
+  pub name: String,
+  pub domain: String,
+}
+
+impl MentionData {
+  pub fn is_local(&self) -> bool {
+    Settings::get().hostname.eq(&self.domain)
+  }
+  pub fn full_name(&self) -> String {
+    format!("@{}@{}", &self.name, &self.domain)
+  }
+}
+
+pub fn scrape_text_for_mentions(text: &str) -> Vec<MentionData> {
+  let mut out: Vec<MentionData> = Vec::new();
+  for caps in WEBFINGER_USER_REGEX.captures_iter(text) {
+    out.push(MentionData {
+      name: caps["name"].to_string(),
+      domain: caps["domain"].to_string(),
+    });
+  }
+  out.into_iter().unique().collect()
+}
+
 #[cfg(test)]
 mod tests {
-  use crate::{extract_usernames, is_email_regex, remove_slurs, slur_check, slurs_vec_to_str};
+  use crate::{
+    is_email_regex, remove_slurs, scrape_text_for_mentions, slur_check, slurs_vec_to_str,
+  };
+
+  #[test]
+  fn test_mentions_regex() {
+    let text = "Just read a great blog post by [@tedu@honk.teduangst.com](/u/test). And another by !test_community@fish.teduangst.com . Another [@lemmy@lemmy_alpha:8540](/u/fish)";
+    let mentions = scrape_text_for_mentions(text);
+
+    assert_eq!(mentions[0].name, "tedu".to_string());
+    assert_eq!(mentions[0].domain, "honk.teduangst.com".to_string());
+    assert_eq!(mentions[1].domain, "lemmy_alpha:8540".to_string());
+  }
 
   #[test]
   fn test_email() {
@@ -287,13 +313,6 @@ mod tests {
     }
   }
 
-  #[test]
-  fn test_extract_usernames() {
-    let usernames = extract_usernames("this is a user mention for [/u/testme](/u/testme) and thats all. Oh [/u/another](/u/another) user. And the first again [/u/testme](/u/testme) okay");
-    let expected = vec!["another", "testme"];
-    assert_eq!(usernames, expected);
-  }
-
   // These helped with testing
   // #[test]
   // fn test_iframely() {
@@ -320,4 +339,7 @@ lazy_static! {
   static ref EMAIL_REGEX: Regex = Regex::new(r"^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$").unwrap();
   static ref SLUR_REGEX: Regex = RegexBuilder::new(r"(fag(g|got|tard)?|maricos?|cock\s?sucker(s|ing)?|nig(\b|g?(a|er)?(s|z)?)\b|dindu(s?)|mudslime?s?|kikes?|mongoloids?|towel\s*heads?|\bspi(c|k)s?\b|\bchinks?|niglets?|beaners?|\bnips?\b|\bcoons?\b|jungle\s*bunn(y|ies?)|jigg?aboo?s?|\bpakis?\b|rag\s*heads?|gooks?|cunts?|bitch(es|ing|y)?|puss(y|ies?)|twats?|feminazis?|whor(es?|ing)|\bslut(s|t?y)?|\btrann?(y|ies?)|ladyboy(s?)|\b(b|re|r)tard(ed)?s?)").case_insensitive(true).build().unwrap();
   static ref USERNAME_MATCHES_REGEX: Regex = Regex::new(r"/u/[a-zA-Z][0-9a-zA-Z_]*").unwrap();
+  // TODO keep this old one, it didn't work with port well tho
+  // static ref WEBFINGER_USER_REGEX: Regex = Regex::new(r"@(?P<name>[\w.]+)@(?P<domain>[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)").unwrap();
+  static ref WEBFINGER_USER_REGEX: Regex = Regex::new(r"@(?P<name>[\w.]+)@(?P<domain>[a-zA-Z0-9._:-]+)").unwrap();
 }
index 6bb70b9317e5b5621348e63567842708220b120d..999252c472de199f5d8bc3c5b08bde8d99bc8bc6 100644 (file)
@@ -142,7 +142,6 @@ fn get_feed_community(
 ) -> Result<ChannelBuilder, failure::Error> {
   let site_view = SiteView::read(&conn)?;
   let community = Community::read_from_name(&conn, &community_name)?;
-  let community_url = community.get_url();
 
   let posts = PostQueryBuilder::create(&conn)
     .listing_type(ListingType::All)
@@ -155,7 +154,7 @@ fn get_feed_community(
   let mut channel_builder = ChannelBuilder::default();
   channel_builder
     .title(&format!("{} - {}", site_view.name, community.name))
-    .link(community_url)
+    .link(community.actor_id)
     .items(items);
 
   if let Some(community_desc) = community.description {
index 109dbd76d4d4c44e513309d2595651ddb4154ff1..957bf1ba8974f4379e6636b58e1a4e4826a27631 100644 (file)
@@ -1,6 +1,8 @@
 use crate::api::{Oper, Perform};
 use crate::apub::get_apub_protocol_string;
+use crate::db::community::Community;
 use crate::db::site_view::SiteView;
+use crate::db::user::User_;
 use crate::rate_limit::rate_limiter::RateLimiter;
 use crate::websocket::{server::ChatServer, WebsocketInfo};
 use crate::{get_ip, markdown_to_html, version, Settings};
@@ -13,11 +15,10 @@ use diesel::{
   r2d2::{ConnectionManager, Pool},
   PgConnection,
 };
-use log::{error, info};
+use log::{debug, error, info};
 use regex::Regex;
 use rss::{CategoryBuilder, ChannelBuilder, GuidBuilder, Item, ItemBuilder};
 use serde::{Deserialize, Serialize};
-use serde_json::json;
 use std::str::FromStr;
 use std::sync::{Arc, Mutex};
 use std::time::{Duration, Instant};
index 7bdce2067b2f247d21d1fec633cd6e65ddfd8959..439064ad732e69f8a194e70d2706b3e4c633bb7d 100644 (file)
@@ -1,11 +1,27 @@
 use super::*;
-use crate::db::community::Community;
 
 #[derive(Deserialize)]
 pub struct Params {
   resource: String,
 }
 
+#[derive(Serialize, Deserialize, Debug)]
+pub struct WebFingerResponse {
+  pub subject: String,
+  pub aliases: Vec<String>,
+  pub links: Vec<WebFingerLink>,
+}
+
+#[derive(Serialize, Deserialize, Debug)]
+pub struct WebFingerLink {
+  pub rel: Option<String>,
+  #[serde(rename(serialize = "type", deserialize = "type"))]
+  pub type_: Option<String>,
+  pub href: Option<String>,
+  #[serde(skip_serializing_if = "Option::is_none")]
+  pub template: Option<String>,
+}
+
 pub fn config(cfg: &mut web::ServiceConfig) {
   if Settings::get().federation.enabled {
     cfg.route(
@@ -21,6 +37,11 @@ lazy_static! {
     Settings::get().hostname
   ))
   .unwrap();
+  static ref WEBFINGER_USER_REGEX: Regex = Regex::new(&format!(
+    "^acct:([a-z0-9_]{{3, 20}})@{}$",
+    Settings::get().hostname
+  ))
+  .unwrap();
 }
 
 /// Responds to webfinger requests of the following format. There isn't any real documentation for
@@ -36,46 +57,58 @@ async fn get_webfinger_response(
   let res = web::block(move || {
     let conn = db.get()?;
 
-    let regex_parsed = WEBFINGER_COMMUNITY_REGEX
+    let community_regex_parsed = WEBFINGER_COMMUNITY_REGEX
+      .captures(&info.resource)
+      .map(|c| c.get(1))
+      .flatten();
+
+    let user_regex_parsed = WEBFINGER_USER_REGEX
       .captures(&info.resource)
       .map(|c| c.get(1))
       .flatten();
-    let community_name = match regex_parsed {
-      Some(c) => c.as_str(),
-      None => return Err(format_err!("not_found")),
-    };
 
-    // Make sure the requested community exists.
-    let community = match Community::read_from_name(&conn, &community_name) {
-      Ok(o) => o,
-      Err(_) => return Err(format_err!("not_found")),
+    let url = if let Some(community_name) = community_regex_parsed {
+      // Make sure the requested community exists.
+      let community = match Community::read_from_name(&conn, &community_name.as_str()) {
+        Ok(o) => o,
+        Err(_) => return Err(format_err!("not_found")),
+      };
+      community.actor_id
+    } else if let Some(user_name) = user_regex_parsed {
+      // Make sure the requested user exists.
+      let user = match User_::read_from_name(&conn, &user_name.as_str()) {
+        Ok(o) => o,
+        Err(_) => return Err(format_err!("not_found")),
+      };
+      user.actor_id
+    } else {
+      return Err(format_err!("not_found"));
     };
 
-    let community_url = community.get_url();
+    let wf_res = WebFingerResponse {
+      subject: info.resource.to_owned(),
+      aliases: vec![url.to_owned()],
+      links: vec![
+        WebFingerLink {
+          rel: Some("http://webfinger.net/rel/profile-page".to_string()),
+          type_: Some("text/html".to_string()),
+          href: Some(url.to_owned()),
+          template: None,
+        },
+        WebFingerLink {
+          rel: Some("self".to_string()),
+          type_: Some("application/activity+json".to_string()),
+          href: Some(url),
+          template: None,
+        }, // TODO: this also needs to return the subscribe link once that's implemented
+           //{
+           //  "rel": "http://ostatus.org/schema/1.0/subscribe",
+           //  "template": "https://my_instance.com/authorize_interaction?uri={uri}"
+           //}
+      ],
+    };
 
-    Ok(json!({
-    "subject": info.resource,
-    "aliases": [
-      community_url,
-    ],
-    "links": [
-      {
-        "rel": "http://webfinger.net/rel/profile-page",
-        "type": "text/html",
-        "href": community.get_url(),
-      },
-      {
-        "rel": "self",
-        "type": "application/activity+json",
-        "href": community_url
-      }
-      // TODO: this also needs to return the subscribe link once that's implemented
-      //{
-      //  "rel": "http://ostatus.org/schema/1.0/subscribe",
-      //  "template": "https://my_instance.com/authorize_interaction?uri={uri}"
-      //}
-    ]
-    }))
+    Ok(wf_res)
   })
   .await
   .map(|json| HttpResponse::Ok().json(json))
index a95ff9ee820c16dba14c8b1192711395f278509c..8b94965a48f5bf5376561b5999dc5c3d0a05426f 100644 (file)
@@ -143,7 +143,7 @@ impl WSSession {
       // check client heartbeats
       if Instant::now().duration_since(act.hb) > CLIENT_TIMEOUT {
         // heartbeat timed out
-        error!("Websocket Client heartbeat failed, disconnecting!");
+        debug!("Websocket Client heartbeat failed, disconnecting!");
 
         // notify chat server
         act.cs_addr.do_send(Disconnect {
index dcfb5e628fd032d0ebf2176eebfbad89d0ebbd1a..d5da17c7f0af19db5fbaab09571e4b81f4e6dd4b 100644 (file)
@@ -22,6 +22,7 @@ import {
   EditPrivateMessageForm,
   PrivateMessageResponse,
   PrivateMessagesResponse,
+  GetUserMentionsResponse,
 } from '../interfaces';
 
 let lemmyAlphaUrl = 'http://localhost:8540';
@@ -379,6 +380,44 @@ describe('main', () => {
       expect(getPostResAlpha.comments[0].community_local).toBe(false);
       expect(getPostResAlpha.comments[0].creator_local).toBe(false);
       expect(getPostResAlpha.comments[0].score).toBe(1);
+
+      // Lemmy alpha responds to their own comment, but mentions lemmy beta.
+      // Make sure lemmy beta gets that in their inbox.
+      let mentionContent = 'A test mention of @lemmy_beta@lemmy_beta:8550';
+      let mentionCommentForm: CommentForm = {
+        content: mentionContent,
+        post_id: 2,
+        parent_id: createResponse.comment.id,
+        auth: lemmyAlphaAuth,
+      };
+
+      let createMentionRes: CommentResponse = await fetch(
+        `${lemmyAlphaApiUrl}/comment`,
+        {
+          method: 'POST',
+          headers: {
+            'Content-Type': 'application/json',
+          },
+          body: wrapper(mentionCommentForm),
+        }
+      ).then(d => d.json());
+
+      expect(createMentionRes.comment.content).toBe(mentionContent);
+      expect(createMentionRes.comment.community_local).toBe(false);
+      expect(createMentionRes.comment.creator_local).toBe(true);
+      expect(createMentionRes.comment.score).toBe(1);
+
+      // Make sure lemmy beta sees that new mention
+      let getMentionUrl = `${lemmyBetaApiUrl}/user/mention?sort=New&unread_only=false&auth=${lemmyBetaAuth}`;
+      let getMentionsRes: GetUserMentionsResponse = await fetch(getMentionUrl, {
+        method: 'GET',
+      }).then(d => d.json());
+
+      // The newest show up first
+      expect(getMentionsRes.mentions[0].content).toBe(mentionContent);
+      expect(getMentionsRes.mentions[0].community_local).toBe(true);
+      expect(getMentionsRes.mentions[0].creator_local).toBe(false);
+      expect(getMentionsRes.mentions[0].score).toBe(1);
     });
   });
 
@@ -413,9 +452,9 @@ describe('main', () => {
         method: 'GET',
       }).then(d => d.json());
 
-      expect(getPostRes.comments[1].content).toBe(content);
-      expect(getPostRes.comments[1].community_local).toBe(true);
-      expect(getPostRes.comments[1].creator_local).toBe(false);
+      expect(getPostRes.comments[2].content).toBe(content);
+      expect(getPostRes.comments[2].community_local).toBe(true);
+      expect(getPostRes.comments[2].creator_local).toBe(false);
     });
   });
 
index 277da9874bcb298bbb5801709c4347e403d2f0fe..54322c8b2a88243b9af36fdc0b720fd141a85695 100644 (file)
@@ -502,8 +502,8 @@ export function setupTribute(): Tribute {
         trigger: '@',
         selectTemplate: (item: any) => {
           let link = item.original.local
-            ? `[@${item.original.key}](/u/${item.original.key})`
-            : `[@${item.original.key}](/user/${item.original.id})`;
+            ? `[${item.original.key}](/u/${item.original.name})`
+            : `[${item.original.key}](/user/${item.original.id})`;
           return link;
         },
         values: (text: string, cb: any) => {
@@ -520,8 +520,8 @@ export function setupTribute(): Tribute {
         trigger: '!',
         selectTemplate: (item: any) => {
           let link = item.original.local
-            ? `[!${item.original.key}](/c/${item.original.key})`
-            : `[!${item.original.key}](/community/${item.original.id})`;
+            ? `[${item.original.key}](/c/${item.original.name})`
+            : `[${item.original.key}](/community/${item.original.id})`;
           return link;
         },
         values: (text: string, cb: any) => {
@@ -565,9 +565,9 @@ function userSearch(text: string, cb: any) {
         if (res.op == UserOperation.Search) {
           let data = res.data as SearchResponse;
           let users = data.users.map(u => {
-            let name_ = u.local ? u.name : `${u.name}@${hostname(u.actor_id)}`;
             return {
-              key: name_,
+              key: `@${u.name}@${hostname(u.actor_id)}`,
+              name: u.name,
               local: u.local,
               id: u.id,
             };
@@ -602,9 +602,9 @@ function communitySearch(text: string, cb: any) {
         if (res.op == UserOperation.Search) {
           let data = res.data as SearchResponse;
           let communities = data.communities.map(c => {
-            let name_ = c.local ? c.name : `${c.name}@${hostname(c.actor_id)}`;
             return {
-              key: name_,
+              key: `!${c.name}@${hostname(c.actor_id)}`,
+              name: c.name,
               local: c.local,
               id: c.id,
             };
@@ -859,5 +859,8 @@ export function previewLines(text: string, lines: number = 3): string {
 }
 
 export function hostname(url: string): string {
-  return new URL(url).hostname;
+  let cUrl = new URL(url);
+  return window.location.port
+    ? `${cUrl.hostname}:${cUrl.port}`
+    : `${cUrl.hostname}`;
 }