虽然叫水源助手,但是加上一行
@match https://xjtu.app/*
就能很好的使用了。水源销加跑了,没有原帖链接,有源友看到了欢迎搬一下,添加了很多常用功能,比如看到表情都是谁贴的
怎么复活了
在这个平台搬水源链接他们真的打得开吗
可以直接把主楼复制了丢过来
太长了(懒
2022 年 4 月 12 日更新:
目前重制版的水源助手已经与原始版本完成合并,现在就是一个版本了,大家可以直接继续使用原版地址上的脚本:
之前从重制版本地址安装脚本的用户,应该是会被 Greasy Fork 自动重定向到现在合并的原版地址上,可以自行检查一下。
以下为 2022 年 3 月 25 日原始帖子内容:
“水源助手”脚本是无私奉献的 @bluecat 同学创立的作品。最初该脚本用于 发帖时使用 QQ 表情,后续加入了 在帖子下方贴任意表情、默认展开显示点赞人及贴表情人、反和谐词、去除模糊 等等功能。我已经使用“水源助手”脚本很长时间了,这个脚本很大程度上提升了我刷水源的体验。我甚至还折腾出了 在手机浏览器上使用这个脚本的方法。截至发帖的此时此刻,“水源助手”脚本总安装量已达 113 次。
然而,长期以来,脚本的贴表情功能中存在的一些小缺陷让人时不时感到不爽,例如:
上周我实在忍不住想要解决这些问题。从开源贡献的角度上来说,能够把修改合并进原有的代码是最好的。然而我发现要修改原有的脚本来达成满意的效果已经显得有点困难了,所以狠心重新实现了一版,也便于实现一些其他的改进。我无意取代 bluecat 的“江湖地位”,脚本的鼻祖和主要贡献者仍然是 bluecat。我和 bluecat 以后谁有更多时间来维护脚本是无法确定的。在未来某一天,我可能不再会有时间刷水源,渐渐淡出社区。但从今天的角度来说,我做了这个重制版的脚本,这个脚本也许能够改善其他用户的浏览体验,所以我就把它发布出来,给大家多一个选择。
安装地址:
(重制版本地址已经删去,大家可以直接前往合并的原版地址:水源助手
(沿用 bluecat 的 GPL v3 开源许可证)
Bookmarklet 版本:
javascript:(async()=>{"use strict";const e="shuiyuanHelperConfig";if(window.shuiyuanHelperLoaded)return void console.log("Skipped loading Shuiyuan Helper as it has already been loaded.");window.shuiyuanHelperLoaded=!0;const a=document.documentElement.classList.contains("mobile-device"),o=()=>JSON.parse(document.getElementById("data-preloaded").getAttribute("data-preloaded"));let n=null;let t=null;const i=()=>{if(t)return t;try{t=JSON.parse(o().site).watched_words_replace}catch(e){console.error(e),console.warn("Could not parse watched words from source, falling back to built-in watched words."),t={[`(?:\\W|^)(${decodeURIComponent("%E5%82%BB")}\\S*逼)(?=\\W|$)`]:"大笨蛋",[`(?:\\W|^)(${decodeURIComponent("%E7%A5%9E")}经病)(?=\\W|$)`]:"小变态",[`(?:\\W|^)(${decodeURIComponent("%E7%8E%8B")}八蛋)(?=\\W|$)`]:"小可爱",[`(?:\\W|^)(${decodeURIComponent("%E5%86%9A")}家铲)(?=\\W|$)`]:"萌萌哒",[`(?:\\W|^)(${decodeURIComponent("%E5%A6%88")}卖批)(?=\\W|$)`]:"不要啦",[`(?:\\W|^)(${decodeURIComponent("%E5%AD%A4")}儿)(?=\\W|$)`]:"小宝贝"}}return t},r=e=>new Promise((a=>setTimeout(a,e))),s=(e,a)=>a?.length?[...e.querySelectorAll(a.join(", "))]:[],l=(e,a)=>a?.length?[...e.querySelectorAll(a.map((e=>`.${e}`)).join(", "))]:[],_=async e=>{const a=await Promise.allSettled(e);for(const e of a)"rejected"===e.status&&console.error(e.reason)},c=[{id:"expand-who-liked",description:"默认展开点赞人列表",enabledByDefault:!0,matchClass:"like-count",onMatch:e=>{e.shuiyuanHelperHandled||(e.shuiyuanHelperHandled=!0,e.click())}},{id:"show-liked-usernames",description:"显示点赞人用户名",enabledByDefault:!0,matchClass:"who-liked",onMatch:e=>{const a=e=>{if(e.children.length>1)return;const a=document.createElement("span");a.style.margin="0 5px",a.innerText=e.firstElementChild.getAttribute("title"),e.appendChild(a)};if(!e.shuiyuanHelperObserver){const o=new MutationObserver((e=>{for(const o of e)if("childList"===o.type)for(const e of o.addedNodes)e.matches?.("a.trigger-user-card")&&a(e)}));o.observe(e,{subtree:!0,childList:!0}),e.shuiyuanHelperObserver=o}for(const o of e.querySelectorAll("a.trigger-user-card"))a(o)}},{id:"show-retort-users",description:"显示所有贴表情人",enabledByDefault:!0,matchClass:"post-retort-container",onMatch:e=>{const a=e.closest(".topic-area"),o=parseInt(a.getAttribute("data-topic-id"),10);a.shuiyuanHelperData||(a.shuiyuanHelperData={retortFetchQueue:[],retortFetchTimeoutID:null});const n=a.shuiyuanHelperData,t=e.closest("article"),i=parseInt(t.getAttribute("data-post-id"),10),s=e=>e.matches?.("button.post-retort"),l=(e,a,o)=>{const n=document.createElement("div");n.shuiyuanHelperGenerated=!0,a.shuiyuanHelperGenerated=!0,n.appendChild(a);const t=a.firstElementChild.alt.slice(1,-1),i=o.get(t)||[];for(const e of i){const a=document.createElement("a");a.href=`/u/${encodeURIComponent(e)}`,a.setAttribute("data-user-card",e),a.innerText=e,n.appendChild(a),n.appendChild(document.createTextNode(";"))}e.appendChild(n)},_=async()=>{const e=n.retortFetchQueue.splice(0,20),t=await(async(e,a)=>{let o=0;for(;;){const n=await fetch(e,{method:a?.method??"GET",headers:{"Discourse-Present":"true","Discourse-Logged-In":"true","X-Requested-With":"XMLHttpRequest","X-CSRF-Token":document.querySelector("meta[name=csrf-token]").content,...a?.headers},body:a?.body,mode:"same-origin",credentials:"include",redirect:"follow"});if(429!==n.status){if(!n.ok)throw new Error(`${n.status}${n.statusText?` ${n.statusText}`:""}`);return n}if(o+=1,o>15)throw new Error("Max retries exceeded on 429 errors");await r(1e3*1.2**(o-1))}})(`/t/${o}/posts.json?${e.map((e=>`post_ids%5B%5D=${e}`)).join("&")}`),i=(await t.json())?.post_stream?.posts||[];for(const e of i){const o=new Map((e?.retorts||[]).map((e=>[e.emoji,e.usernames]))),n=a.querySelector(`article[data-post-id="${e.id}"] .post-retort-container`);if(n)for(const e of[...n.children])s(e)?l(n,e,o):e.matches("div")&&e.shuiyuanHelperGenerated?(l(n,e.firstElementChild,o),e.remove()):e.remove()}n.retortFetchQueue.length>0?n.retortFetchTimeoutID=setTimeout(_,300):n.retortFetchTimeoutID=null},c=()=>{const e=n.retortFetchQueue;e.includes(i)||e.push(i),null===n.retortFetchTimeoutID&&(n.retortFetchTimeoutID=setTimeout(_,300))};if(!e.shuiyuanHelperObserver){const a=new MutationObserver((e=>{let a=!1;for(const o of e)if("childList"===o.type)for(const e of o.addedNodes)s(e)&&!e.shuiyuanHelperGenerated&&(a=!0);a&&c()}));a.observe(e,{subtree:!0,childList:!0}),e.shuiyuanHelperObserver=a}e.firstElementChild&&c()}},{id:"retort-all-emojis",description:"可在帖子下方贴任意表情",enabledByDefault:!0,matchClass:"has-limited-set",onMatch:e=>{e.classList.remove("has-limited-set");const a=document.createElement("div");a.classList.add("emojis-container");for(const{section:e,emojis:t}of(()=>{if(n)return n;try{const e=window.require("discourse/templates/components/emoji-group-sections").default(window.Discourse).parsedLayout.block.statements,a=window.require("I18n"),t=[];t.push({section:"默认",emojis:JSON.parse(o().siteSettings).retort_allowed_emojis.split("|")});let i=null,r=[];const s=()=>{null!==i&&(t.push({section:i,emojis:r}),r=[])};for(const o of e){const e=o[1];Array.isArray(e)&&("i18n"===e[1]?(s(),i=a.t(e[2][0])):"replace-emoji"===e[1]&&r.push(e[2][0].slice(1,-1)))}s(),n=t}catch(e){console.error(e),console.warn("Could not parse all emojis from source, falling back to built-in emojis."),n=[{section:"默认",emojis:["sweat_smile","joy","sob","+1","-1","rage","poop","dog","clown_face","smiling_face_with_three_hearts","rofl","cow","kissing_heart","tada"]},{section:"笑脸与情感",emojis:["grinning","smiley","grinning_face_with_smiling_eyes","grin","laughing","sweat_smile","rofl","joy","slightly_smiling_face","upside_down_face","wink","blush","innocent","smiling_face_with_three_hearts","heart_eyes","star_struck","kissing_heart","kissing","relaxed","kissing_closed_eyes","kissing_smiling_eyes","yum","stuck_out_tongue","stuck_out_tongue_winking_eye","crazy_face","stuck_out_tongue_closed_eyes","money_mouth_face","hugs","face_with_hand_over_mouth","shushing_face","thinking","zipper_mouth_face","face_with_raised_eyebrow","neutral_face","expressionless","no_mouth","smirk","unamused","roll_eyes","grimacing","lying_face","relieved","pensive","sleepy","drooling_face","sleeping","mask","face_with_thermometer","face_with_head_bandage","nauseated_face","face_vomiting","sneezing_face","hot_face","cold_face","woozy_face","dizzy_face","exploding_head","cowboy_hat_face","partying_face","sunglasses","nerd_face","face_with_monocle","confused","worried","slightly_frowning_face","frowning_face","open_mouth","hushed","astonished","flushed","pleading_face","frowning","frowning_face_with_open_mouth","anguished","fearful","cold_sweat","disappointed_relieved","cry","sob","scream","confounded","persevere","disappointed","sweat","weary","tired_face","yawning_face","triumph","rage","angry","face_with_symbols_over_mouth","smiling_imp","imp","skull","skull_and_crossbones","poop","clown_face","japanese_ogre","japanese_goblin","ghost","alien","space_invader","robot","smiley_cat","smile_cat","joy_cat","heart_eyes_cat","smirk_cat","kissing_cat","scream_cat","crying_cat_face","pouting_cat","see_no_evil","hear_no_evil","speak_no_evil","kiss","love_letter","cupid","gift_heart","sparkling_heart","heartpulse","heartbeat","revolving_hearts","two_hearts","heart_decoration","heavy_heart_exclamation","broken_heart","heart","orange_heart","yellow_heart","green_heart","blue_heart","purple_heart","brown_heart","black_heart","white_heart","100","anger","boom","dizzy","sweat_drops","dash","hole","bomb","speech_balloon","left_speech_bubble","right_anger_bubble","thought_balloon","zzz"]},{section:"人与身体",emojis:["wave","raised_back_of_hand","raised_hand_with_fingers_splayed","raised_hand","vulcan_salute","ok_hand","pinching_hand","v","crossed_fingers","love_you_gesture","metal","call_me_hand","point_left","point_right","point_up_2","fu","point_down","point_up","+1","-1","fist","facepunch","fist_left","fist_right","clap","raised_hands","open_hands","palms_up_together","handshake","pray","writing_hand","nail_care","selfie","muscle","mechanical_arm","mechanical_leg","leg","foot","ear","hear_with_hearing_aid","nose","brain","tooth","bone","eyes","eye","tongue","lips","baby","child","boy","girl","adult","blonde_man","man","bearded_person","man_red_haired","man_curly_haired","man_white_haired","man_bald","woman","woman_red_haired","woman_curly_haired","woman_white_haired","woman_bald","blonde_woman","older_adult","older_man","older_woman","frowning_man","frowning_woman","pouting_man","pouting_woman","no_good_man","no_good_woman","ok_man","ok_woman","tipping_hand_man","tipping_hand_woman","raising_hand_man","raising_hand_woman","deaf_man","deaf_woman","bowing_man","bowing_woman","man_facepalming","woman_facepalming","man_shrugging","woman_shrugging","man_health_worker","woman_health_worker","man_student","woman_student","man_teacher","woman_teacher","man_judge","woman_judge","man_farmer","woman_farmer","man_cook","woman_cook","man_mechanic","woman_mechanic","man_factory_worker","woman_factory_worker","man_office_worker","woman_office_worker","man_scientist","woman_scientist","man_technologist","woman_technologist","man_singer","woman_singer","man_artist","woman_artist","man_pilot","woman_pilot","man_astronaut","woman_astronaut","man_firefighter","woman_firefighter","policeman","policewoman","male_detective","female_detective","guardsman","guardswoman","construction_worker_man","construction_worker_woman","prince","princess","man_with_turban","woman_with_turban","man_with_gua_pi_mao","woman_with_headscarf","man_in_tuxedo","bride_with_veil","pregnant_woman","breast_feeding","angel","santa","mrs_claus","superhero","man_superhero","woman_superhero","supervillain","man_supervillain","woman_supervillain","mage","woman_mage","fairy","vampire","mermaid","merman","elf","genie","woman_genie","zombie","woman_zombie","massage_man","massage_woman","haircut_man","haircut_woman","walking_man","walking_woman","man_standing","woman_standing","man_kneeling","woman_kneeling","man_with_probing_cane","woman_with_probing_cane","man_in_motorized_wheelchair","woman_in_motorized_wheelchair","man_in_manual_wheelchair","woman_in_manual_wheelchair","running_man","running_woman","dancer","man_dancing","business_suit_levitating","dancing_women","dancing_men","person_in_steamy_room","woman_in_steamy_room","person_climbing","woman_climbing","person_fencing","horse_racing","skier","snowboarder","golfing_man","golfing_woman","surfing_man","surfing_woman","rowing_man","rowing_woman","swimming_man","swimming_woman","basketball_man","basketball_woman","weight_lifting_man","weight_lifting_woman","biking_man","biking_woman","mountain_biking_man","mountain_biking_woman","man_cartwheeling","woman_cartwheeling","men_wrestling","women_wrestling","man_playing_water_polo","woman_playing_water_polo","man_playing_handball","woman_playing_handball","man_juggling","woman_juggling","person_in_lotus_position","woman_in_lotus_position","bath","sleeping_bed","people_holding_hands","two_women_holding_hands","couple","two_men_holding_hands","couplekiss_man_woman","couplekiss_man_man","couplekiss_woman_woman","couple_with_heart_woman_man","couple_with_heart_man_man","couple_with_heart_woman_woman","family_man_woman_boy","family_man_woman_girl","family_man_woman_girl_boy","family_man_woman_boy_boy","family_man_woman_girl_girl","family_man_man_boy","family_man_man_girl","family_man_man_girl_boy","family_man_man_boy_boy","family_man_man_girl_girl","family_woman_woman_boy","family_woman_woman_girl","family_woman_woman_girl_boy","family_woman_woman_boy_boy","family_woman_woman_girl_girl","family_man_boy","family_man_boy_boy","family_man_girl","family_man_girl_boy","family_man_girl_girl","family_woman_boy","family_woman_boy_boy","family_woman_girl","family_woman_girl_boy","family_woman_girl_girl","speaking_head","bust_in_silhouette","busts_in_silhouette","footprints"]},{section:"动物与自然",emojis:["monkey_face","monkey","gorilla","orangutan","dog","dog2","guide_dog","service_dog","poodle","wolf","fox_face","raccoon","cat","cat2","lion","tiger","tiger2","leopard","horse","racehorse","unicorn","zebra","deer","cow","ox","water_buffalo","cow2","pig","pig2","boar","pig_nose","ram","sheep","goat","dromedary_camel","camel","llama","giraffe","elephant","rhinoceros","hippopotamus","mouse","mouse2","rat","hamster","rabbit","rabbit2","chipmunk","hedgehog","bat","bear","koala","panda_face","sloth","otter","skunk","kangaroo","badger","paw_prints","turkey","chicken","rooster","hatching_chick","baby_chick","hatched_chick","bird","penguin","dove","eagle","duck","swan","owl","flamingo","peacock","parrot","frog","crocodile","turtle","lizard","snake","dragon_face","dragon","sauropod","t_rex","whale","whale2","dolphin","fish","tropical_fish","blowfish","shark","octopus","shell","snail","butterfly","bug","ant","honeybee","beetle","cricket","spider","spider_web","scorpion","mosquito","microbe","bouquet","cherry_blossom","white_flower","rosette","rose","wilted_flower","hibiscus","sunflower","blossom","tulip","seedling","evergreen_tree","deciduous_tree","palm_tree","cactus","ear_of_rice","herb","shamrock","four_leaf_clover","maple_leaf","fallen_leaf","leaves"]},{section:"食物和饮料",emojis:["grapes","melon","watermelon","tangerine","lemon","banana","pineapple","mango","apple","green_apple","pear","peach","cherries","strawberry","kiwi_fruit","tomato","coconut","avocado","eggplant","potato","carrot","corn","hot_pepper","cucumber","leafy_green","broccoli","garlic","onion","mushroom","peanuts","chestnut","bread","croissant","baguette_bread","pretzel","bagel","pancakes","waffle","cheese","meat_on_bone","poultry_leg","cut_of_meat","bacon","hamburger","fries","pizza","hotdog","sandwich","taco","burrito","stuffed_flatbread","falafel","egg","fried_egg","shallow_pan_of_food","stew","bowl_with_spoon","green_salad","popcorn","butter","salt","canned_food","bento","rice_cracker","rice_ball","rice","curry","ramen","spaghetti","sweet_potato","oden","sushi","fried_shrimp","fish_cake","moon_cake","dango","dumpling","fortune_cookie","takeout_box","crab","lobster","shrimp","squid","oyster","icecream","shaved_ice","ice_cream","doughnut","cookie","birthday","cake","cupcake","pie","chocolate_bar","candy","lollipop","custard","honey_pot","baby_bottle","milk_glass","coffee","tea","sake","champagne","wine_glass","cocktail","tropical_drink","beer","beers","clinking_glasses","tumbler_glass","cup_with_straw","beverage_box","maté","ice_cube","chopsticks","plate_with_cutlery","fork_and_knife","spoon","hocho","amphora"]},{section:"旅行与地点",emojis:["earth_africa","earth_americas","earth_asia","globe_with_meridians","world_map","japan","compass","mountain_snow","mountain","volcano","mount_fuji","camping","beach_umbrella","desert","desert_island","national_park","stadium","classical_building","building_construction","brick","houses","derelict_house","house","house_with_garden","office","post_office","european_post_office","hospital","bank","hotel","love_hotel","convenience_store","school","department_store","factory","japanese_castle","european_castle","wedding","tokyo_tower","statue_of_liberty","church","mosque","hindu_temple","synagogue","shinto_shrine","kaaba","fountain","tent","foggy","night_with_stars","cityscape","sunrise_over_mountains","sunrise","city_sunset","city_sunrise","bridge_at_night","hotsprings","carousel_horse","ferris_wheel","roller_coaster","barber","circus_tent","steam_locomotive","railway_car","bullettrain_side","bullettrain_front","train2","metro","light_rail","station","tram","monorail","mountain_railway","train","bus","oncoming_bus","trolleybus","minibus","ambulance","fire_engine","police_car","oncoming_police_car","taxi","oncoming_taxi","red_car","oncoming_automobile","blue_car","truck","articulated_lorry","tractor","racing_car","motorcycle","motor_scooter","manual_wheelchair","motorized_wheelchair","auto_rickshaw","bike","kick_scooter","skateboard","busstop","motorway","railway_track","oil_drum","fuelpump","rotating_light","traffic_light","vertical_traffic_light","stop_sign","construction","anchor","sailboat","canoe","speedboat","passenger_ship","ferry","motor_boat","ship","airplane","small_airplane","flight_departure","flight_arrival","parachute","seat","helicopter","suspension_railway","mountain_cableway","aerial_tramway","artificial_satellite","rocket","flying_saucer","bellhop_bell","luggage","hourglass","hourglass_flowing_sand","watch","alarm_clock","stopwatch","timer_clock","mantelpiece_clock","clock12","clock1230","clock1","clock130","clock2","clock230","clock3","clock330","clock4","clock430","clock5","clock530","clock6","clock630","clock7","clock730","clock8","clock830","clock9","clock930","clock10","clock1030","clock11","clock1130","new_moon","waxing_crescent_moon","first_quarter_moon","waxing_gibbous_moon","full_moon","waning_gibbous_moon","last_quarter_moon","waning_crescent_moon","crescent_moon","new_moon_with_face","first_quarter_moon_with_face","last_quarter_moon_with_face","thermometer","sunny","full_moon_with_face","sun_with_face","ringer_planet","star","star2","stars","milky_way","cloud","partly_sunny","cloud_with_lightning_and_rain","sun_behind_small_cloud","sun_behind_large_cloud","sun_behind_rain_cloud","cloud_with_rain","cloud_with_snow","cloud_with_lightning","tornado","fog","wind_face","cyclone","rainbow","closed_umbrella","open_umbrella","umbrella","parasol_on_ground","zap","snowflake","snowman_with_snow","snowman","comet","fire","droplet","ocean"]},{section:"活动",emojis:["jack_o_lantern","christmas_tree","fireworks","sparkler","firecracker","sparkles","balloon","tada","confetti_ball","tanabata_tree","bamboo","dolls","flags","wind_chime","rice_scene","red_gift_envelope","ribbon","gift","reminder_ribbon","tickets","ticket","medal_military","trophy","medal_sports","1st_place_medal","2nd_place_medal","3rd_place_medal","soccer","baseball","softball","basketball","volleyball","football","rugby_football","tennis","flying_disc","bowling","cricket_bat_and_ball","field_hockey","ice_hockey","lacrosse","ping_pong","badminton","boxing_glove","martial_arts_uniform","goal_net","golf","ice_skate","fishing_pole_and_fish","diving_mask","running_shirt_with_sash","ski","sled","curling_stone","dart","yo-yo","kite","8ball","crystal_ball","nazar_amulet","video_game","joystick","slot_machine","game_die","jigsaw","teddy_bear","spades","hearts","diamonds","clubs","chess_pawn","black_joker","mahjong","flower_playing_cards","performing_arts","framed_picture","art","thread","yarn"]},{section:"对象",emojis:["eyeglasses","dark_sunglasses","goggles","lab_coat","safety_vest","necktie","tshirt","jeans","scarf","gloves","coat","socks","dress","kimono","sari","one_piece_swimsuit","briefs","shorts","bikini","womans_clothes","purse","handbag","pouch","shopping","school_satchel","mans_shoe","athletic_shoe","hiking_boot","flat_shoe","high_heel","sandal","ballet_shoes","boot","crown","womans_hat","tophat","mortar_board","billed_cap","rescue_worker_helmet","prayer_beads","lipstick","ring","gem","mute","speaker","sound","loud_sound","loudspeaker","mega","postal_horn","bell","no_bell","musical_score","musical_note","notes","studio_microphone","level_slider","control_knobs","microphone","headphones","radio","saxophone","guitar","musical_keyboard","trumpet","violin","banjo","drum","iphone","calling","phone","telephone_receiver","pager","fax","battery","electric_plug","computer","desktop_computer","printer","keyboard","computer_mouse","trackball","minidisc","floppy_disk","cd","dvd","abacus","movie_camera","film_strip","film_projector","clapper","tv","camera","camera_flash","video_camera","vhs","mag","mag_right","candle","bulb","flashlight","izakaya_lantern","diya_lamp","notebook_with_decorative_cover","closed_book","open_book","green_book","blue_book","orange_book","books","notebook","ledger","page_with_curl","scroll","page_facing_up","newspaper","newspaper_roll","bookmark_tabs","bookmark","label","moneybag","yen","dollar","euro","pound","money_with_wings","credit_card","receipt","chart","currency_exchange","heavy_dollar_sign","email","e-mail","incoming_envelope","envelope_with_arrow","outbox_tray","inbox_tray","package","mailbox","mailbox_closed","mailbox_with_mail","mailbox_with_no_mail","postbox","ballot_box","pencil2","black_nib","fountain_pen","pen","paintbrush","crayon","memo","briefcase","file_folder","open_file_folder","card_index_dividers","date","calendar","spiral_notepad","spiral_calendar","card_index","chart_with_upwards_trend","chart_with_downwards_trend","bar_chart","clipboard","pushpin","round_pushpin","paperclip","paperclips","straight_ruler","triangular_ruler","scissors","card_file_box","file_cabinet","wastebasket","lock","unlock","lock_with_ink_pen","closed_lock_with_key","key","old_key","hammer","axe","pick","hammer_and_pick","hammer_and_wrench","dagger","crossed_swords","gun","bow_and_arrow","shield","wrench","nut_and_bolt","gear","clamp","balance_scale","probing_cane","link","chains","toolbox","magnet","alembic","test_tube","petri_dish","dna","microscope","telescope","satellite","syringe","drop_of_blood","pill","adhesive_bandage","stethoscope","door","bed","couch_and_lamp","chair","toilet","shower","bathtub","razor","lotion_bottle","safety_pin","broom","basket","roll_of_toilet_paper","soap","sponge","fire_extinguisher","shopping_cart","smoking","coffin","funeral_urn","moyai"]},{section:"符号",emojis:["atm","put_litter_in_its_place","potable_water","wheelchair","mens","womens","restroom","baby_symbol","wc","passport_control","customs","baggage_claim","left_luggage","warning","children_crossing","no_entry","no_entry_sign","no_bicycles","no_smoking","do_not_litter","non-potable_water","no_pedestrians","no_mobile_phones","underage","radioactive","biohazard","arrow_up","arrow_upper_right","arrow_right","arrow_lower_right","arrow_down","arrow_lower_left","arrow_left","arrow_upper_left","arrow_up_down","left_right_arrow","leftwards_arrow_with_hook","arrow_right_hook","arrow_heading_up","arrow_heading_down","arrows_clockwise","arrows_counterclockwise","back","end","on","soon","top","place_of_worship","atom_symbol","om","star_of_david","wheel_of_dharma","yin_yang","latin_cross","orthodox_cross","star_and_crescent","peace_symbol","menorah","six_pointed_star","aries","taurus","gemini","cancer","leo","virgo","libra","scorpius","sagittarius","capricorn","aquarius","pisces","ophiuchus","twisted_rightwards_arrows","repeat","repeat_one","arrow_forward","fast_forward","next_track_button","play_or_pause_button","arrow_backward","rewind","previous_track_button","arrow_up_small","arrow_double_up","arrow_down_small","arrow_double_down","pause_button","stop_button","record_button","cinema","low_brightness","high_brightness","signal_strength","vibration_mode","mobile_phone_off","infinity","recycle","fleur_de_lis","trident","name_badge","beginner","o","white_check_mark","ballot_box_with_check","heavy_check_mark","heavy_multiplication_x","x","negative_squared_cross_mark","heavy_plus_sign","heavy_minus_sign","heavy_division_sign","curly_loop","loop","part_alternation_mark","eight_spoked_asterisk","eight_pointed_black_star","sparkle","bangbang","interrobang","question","grey_question","grey_exclamation","exclamation","wavy_dash","copyright","registered","tm","hash","asterisk","zero","one","two","three","four","five","six","seven","eight","nine","keycap_ten","capital_abcd","abcd","1234","symbols","abc","a","ab","b","cl","cool","free","information_source","id","m","new","ng","o2","ok","parking","sos","up","vs","koko","sa","u6708","u6709","u6307","ideograph_advantage","u5272","u7121","u7981","accept","u7533","u5408","u7a7a","congratulations","secret","u55b6","u6e80","red_circle","orange_circle","yellow_circle","green_circle","large_blue_circle","purple_circle","brown_circle","black_circle","white_circle","red_square","orange_square","yellow_square","green_square","blue_square","purple_square","brown_square","black_large_square","white_large_square","black_medium_square","white_medium_square","black_medium_small_square","white_medium_small_square","black_small_square","white_small_square","large_orange_diamond","large_blue_diamond","small_orange_diamond","small_blue_diamond","small_red_triangle","small_red_triangle_down","diamond_shape_with_a_dot_inside","radio_button","white_square_button","black_square_button"]},{section:"旗帜",emojis:["checkered_flag","triangular_flag_on_post","crossed_flags","black_flag","white_flag","rainbow_flag","pirate_flag","ascension_island","andorra","united_arab_emirates","afghanistan","antigua_barbuda","anguilla","albania","armenia","angola","antarctica","argentina","american_samoa","austria","australia","aruba","aland_islands","azerbaijan","bosnia_herzegovina","barbados","bangladesh","belgium","burkina_faso","bulgaria","bahrain","burundi","benin","st_barthelemy","bermuda","brunei","bolivia","caribbean_netherlands","brazil","bahamas","bhutan","bouvet_island","botswana","belarus","belize","canada","cocos_islands","congo_kinshasa","central_african_republic","congo_brazzaville","switzerland","cote_divoire","cook_islands","chile","cameroon","cn","colombia","clipperton_island","costa_rica","cuba","cape_verde","curacao","christmas_island","cyprus","czech_republic","de","diego_garcia","djibouti","denmark","dominica","dominican_republic","algeria","ceuta_and_melilla","ecuador","estonia","egypt","western_sahara","eritrea","es","ethiopia","eu","finland","fiji","falkland_islands","micronesia","faroe_islands","fr","gabon","uk","grenada","georgia","french_guiana","guernsey","ghana","gibraltar","greenland","gambia","guinea","guadeloupe","equatorial_guinea","greece","south_georgia_south_sandwich_islands","guatemala","guam","guinea_bissau","guyana","hong_kong","heard_and_mc_donald_islands","honduras","croatia","haiti","hungary","canary_islands","indonesia","ireland","israel","isle_of_man","india","british_indian_ocean_territory","iraq","iran","iceland","it","jersey","jamaica","jordan","jp","kenya","kyrgyzstan","cambodia","kiribati","comoros","st_kitts_nevis","north_korea","kr","kuwait","cayman_islands","kazakhstan","laos","lebanon","st_lucia","liechtenstein","sri_lanka","liberia","lesotho","lithuania","luxembourg","latvia","libya","morocco","monaco","moldova","montenegro","st_martin","madagascar","marshall_islands","macedonia","mali","myanmar","mongolia","macau","northern_mariana_islands","martinique","mauritania","montserrat","malta","mauritius","maldives","malawi","mexico","malaysia","mozambique","namibia","new_caledonia","niger","norfolk_island","nigeria","nicaragua","netherlands","norway","nepal","nauru","niue","new_zealand","oman","panama","peru","french_polynesia","papua_new_guinea","philippines","pakistan","poland","st_pierre_miquelon","pitcairn_islands","puerto_rico","palestinian_territories","portugal","palau","paraguay","qatar","reunion","romania","serbia","ru","rwanda","saudi_arabia","solomon_islands","seychelles","sudan","sweden","singapore","st_helena","slovenia","svalbard_and_jan_mayen","slovakia","sierra_leone","san_marino","senegal","somalia","suriname","south_sudan","sao_tome_principe","el_salvador","sint_maarten","syria","swaziland","tristan_da_cunha","turks_caicos_islands","chad","french_southern_territories","togo","thailand","tajikistan","tokelau","timor_leste","turkmenistan","tunisia","tonga","tr","trinidad_tobago","tuvalu","taiwan","tanzania","ukraine","uganda","us_outlying_islands","united_nations","us","uruguay","uzbekistan","vatican_city","st_vincent_grenadines","venezuela","british_virgin_islands","us_virgin_islands","vietnam","vanuatu","wallis_futuna","samoa","kosovo","yemen","mayotte","south_africa","zambia","zimbabwe"]}]}return n})()){const o=document.createElement("span");o.classList.add("title"),o.innerText=e;const n=document.createElement("div");n.classList.add("section-header"),n.appendChild(o);const i=document.createElement("div");i.classList.add("section-group");for(const e of t){const a=document.createElement("img");a.src=`/images/emoji/google/${encodeURIComponent(e)}.png`,a.title=e,a.classList.add("emoji"),i.appendChild(a)}const r=document.createElement("div");r.classList.add("section"),r.appendChild(n),r.appendChild(i),a.appendChild(r)}const t=document.createElement("div");t.classList.add("loading-container","ember-view"),t.appendChild(a);const i=document.createElement("div");i.classList.add("emoji-picker-emoji-area"),i.setAttribute("role","button"),i.appendChild(t),e.replaceChildren(i)}},{id:"retort-readonly-posts",description:"恢复只读帖子的贴表情功能",enabledByDefault:!0,onInitialize:()=>{var e,a,o;(e=()=>{window.require("discourse/plugins/retort/discourse/lib/retort").default.disabledFor=()=>!1},a=300,o=50,async(...n)=>{let t=0;for(;;)try{return await Promise.resolve(e(...n))}catch(e){if(t+=1,t>o)throw console.error("Max retries exceeded, original error:"),e;await r(a)}})()}},{id:"watched-words-detect-and-replace",description:"对发帖内容中出现的和谐词显示警告,并提供尝试自动反和谐功能",enabledByDefault:!0,matchClass:"d-editor-input",onMatch:e=>{if(e.shuiyuanHelperHandled)return;const a=()=>{e.value=e.value.replaceAll(/\u50bb(\S*) 逼/gu,"傻 $1逼").replaceAll(decodeURIComponent("%E7%A5%9E%E7%BB%8F%E7%97%85"),"神<b></b>经病").replaceAll(decodeURIComponent("%E7%8E%8B%E5%85%AB%E8%9B%8B"),"王<b></b>八蛋").replaceAll(decodeURIComponent("%E5%86%9A%E5%AE%B6%E9%93%B2"),"冚<b></b>家铲").replaceAll(decodeURIComponent("%E5%A6%88%E5%8D%96%E6%89%B9"),"妈<b></b>卖批").replaceAll(decodeURIComponent("%E5%AD%A4%E5%84%BF"),"孤<b></b>儿"),e.dispatchEvent(new Event("keyup",{bubbles:!0,cancelable:!0}))},o=((e,a)=>{let o;return(...n)=>{clearTimeout(o),o=setTimeout((()=>e(...n)),a)}})((()=>{const o=document.getElementsByClassName("composer-action-title")[0];if(o){document.getElementById("shuiyuan-helper-watched-words-warning")?.remove();for(const[n,t]of Object.entries(i()))if(new RegExp(n,"imsu").test(e.value)){const e=document.createElement("span");e.id="shuiyuan-helper-watched-words-warning",e.style.color="var(--danger-medium)",e.appendChild(document.createTextNode(`帖子匹配和谐词 ${n},可能被替换为 ${t},`));const i=document.createElement("a");i.innerText="尝试自动反和谐",i.addEventListener("click",a),e.appendChild(i),o.appendChild(e);break}}}),300);e.shuiyuanHelperHandled=!0,e.addEventListener("keyup",o),o()}},{id:"remove-spoiler-blurred",description:"去除 Spoiler 模糊",enabledByDefault:!0,matchClass:"spoiler-blurred",onMatch:e=>e.classList.remove("spoiler-blurred")},{id:"improve-bilibili-iframe",description:"优化 B 站视频 iframe 显示(仅限电脑端)",enabledByDefault:!0,matchTag:"iframe",onMatch:e=>{if(a)return;const o=e.src||"";let n="";try{n=new URL(o).hostname}catch{}"player.bilibili.com"===n&&(e.style.width="80%",e.style.height="400px")}},{id:"settings-view",description:"水源助手设置面板",enabledByDefault:!0,hideFromSettings:!0,matchClass:"preferences-nav",onMatch:o=>{if(o.shuiyuanHelperHandled)return;const n=a?o.querySelector("ul.drop"):o,t=[...n.getElementsByTagName("li")],i=document.createElement("a");i.classList.add("ember-view"),i.innerText="水源助手";const r=new Set(c.map((e=>e.id)));let s=!1;i.addEventListener("click",(()=>{if(s)return;const n=document.getElementsByClassName("form-vertical")[0];if(!n)return;const l=document.createElement("fieldset");l.classList.add("control-group","shuiyuan-helper-settings");const _=document.createElement("legend");_.classList.add("control-label"),_.style.marginBottom="10px",_.appendChild(document.createTextNode("水源助手设置")),l.appendChild(_);const d=document.createElement("label");d.appendChild(document.createTextNode("注意:水源助手设置仅保存在本地设备,不会通过您的帐户进行同步。")),l.appendChild(d);for(const a of c){if(a.hidden||a.hideFromSettings)continue;const o=document.createElement("input");o.classList.add("ember-checkbox","ember-view"),o.type="checkbox",o.checked=Boolean(a.enabled),o.addEventListener("click",(()=>{const n=JSON.parse(localStorage.getItem(e)??"{}");n[a.id]=o.checked;for(const e of[...Object.keys(n)])r.has(e)||delete n[e];localStorage.setItem(e,JSON.stringify(n))}));const n=document.createElement("label");n.classList.add("checkbox-label"),n.appendChild(o),n.appendChild(document.createTextNode(a.description));const t=document.createElement("div");t.classList.add("controls","ember-view"),t.appendChild(n),l.appendChild(t)}const m=document.createElement("a");m.innerText="重新加载页面",m.addEventListener("click",(()=>window.location.reload()));const u=document.createElement("label");if(u.style.marginTop="10px",u.appendChild(m),u.appendChild(document.createTextNode("以应用更改")),l.appendChild(u),a)o.querySelector("span.selection").firstChild.textContent="水源助手";else{i.classList.add("active");for(const e of t)e.firstElementChild.classList.remove("active")}for(const e of n.children)e.style.display="none";n.appendChild(l),s=!0}));const l=document.createElement("li");l.appendChild(i),n.appendChild(l);const _=e=>{if(!s)return;if(s=!1,a){const a=e.target,n=a.closest?a.closest("li"):a.parentElement;o.querySelector("span.selection").firstChild.textContent=n.innerText.trim()}else i.classList.remove("active");const n=document.getElementsByClassName("form-vertical")[0];if(n){for(const e of n.children)e.style.display="";n.getElementsByClassName("shuiyuan-helper-settings")[0]?.remove()}};for(const e of t)e.addEventListener("click",_);o.shuiyuanHelperHandled=!0}}],d=[],m=[],u=[],h=JSON.parse(localStorage.getItem(e)??"{}");for(const e of c)if(!e.hidden&&(e.enabled=h[e.id]??e.enabledByDefault,e.enabled)){try{u.push(Promise.resolve(e.onInitialize?.()))}catch(e){console.error(e)}e.matchTag?Array.isArray(e.matchTag)||(e.matchTag=[e.matchTag]):e.matchTag=[],e.matchClass?Array.isArray(e.matchClass)||(e.matchClass=[e.matchClass]):e.matchClass=[],e.selector=[...e.matchTag,...e.matchClass.map((e=>`.${e}`))].join(", "),d.push(...e.matchTag),m.push(...e.matchClass)}await _(u);const p=async e=>{if(!e)return;const a=[];for(const o of c)if(o.enabled&&o.selector&&e.matches(o.selector))try{a.push(Promise.resolve(o.onMatch(e)))}catch(e){console.error(e)}await _(a)},g=new RegExp(`(?:^|\\s)${m.map((e=>e.replace(/[.*+?^${}()|[\]\\]/gu,"\\$&"))).join("|")}(?:\\s|$)`,"u"),b=e=>d.includes(e.nodeName.toLowerCase()),w=e=>m.some((a=>e.classList?.contains(a)));new MutationObserver((async e=>{const a=[];for(const n of e)if("childList"===n.type)for(const e of n.addedNodes)(b(e)||w(e))&&a.push(p(e)),e.getElementsByTagName&&a.push(...s(e,d).map(p)),e.getElementsByClassName&&a.push(...l(e,m).map(p));else"attributes"===n.type&&(o=n.oldValue,!g.test(o||"")&&w(n.target)&&a.push(p(n.target)));var o;await _(a)})).observe(document.documentElement,{subtree:!0,childList:!0,attributeFilter:["class"],attributeOldValue:!0}),await _([...s(document,d).map(p),...l(document,m).map(p)])})();
这源于此前的讨论:
https://shuiyuan.sjtu.edu.cn/t/topic/25212/5
https://shuiyuan.sjtu.edu.cn/t/topic/29416/26
现在用户可以自由选择关闭自己不喜欢的功能,开启自己想要的功能。如果其中一个功能崩溃了,还能禁用掉这个功能,同时继续正常使用其他功能。比如,基于这一条消息:
https://shuiyuan.sjtu.edu.cn/t/topic/47785/4
水源下次更新之后“水源助手”的显示贴表情人功能极有可能崩溃。这种时候有功能独立开关就很好。
重制版脚本的所有功能在设计时都会考虑移动端支持,尽可能保证电脑端和移动端都能正常工作。如果移动端不便于实现某功能,该功能不会在移动端生效,以防止显示错乱。
现在贴表情人区域实时更新,自己新贴的表情和别人新贴的表情都会自动更新。
同时修复了上述“点击一条帖子所回复的帖子,再回来,之前帖子下面的贴表情人区域不展开了”的 bug。
服务器接口支持批量查询(这一点也要感谢 bluecat 的提醒),新版脚本每次批量查询不超过 20 条帖子的贴表情,每个主题帖每 300 毫秒最多发起一次查询请求,并有适当的重试机制。这样减小了服务器压力,并使得脚本能够更稳定地加载贴表情人区域。
反和谐词功能由原先的“后台静默进行自动替换”改为给用户显示警告,并提供一键自动反和谐功能。我认为让用户知道和谐词的存在是很有必要的,并且全自动替换的行为并非 100% 的情况下都是用户预期的。如果把“水源助手”本身的代码拿来发帖,而代码又明文包含和谐词的话,自动替换时添加 <b></b>
的行为实际上破坏了代码的语义。让用户手动批准替换是一种更稳妥的行为。
(录屏内容仅供测试,希望大家尽可能使用文明语言交流)
这一功能的行为和 bluecat 原版相同,但仅在电脑端生效。移动端默认的显示效果已经很合理了,调整 iframe 的大小反而显得不和谐。
此功能暂未实现,原因是我本人没有真正使用过这一功能,没有强烈的动机移植过来。当然我也注意到这一功能实际上是“水源助手”的第一个功能,是其最初产生的动机。目前此功能使用率看起来很低,但我不否认它能带来的价值。后续可能考虑加上。
其实 QQ 表情按钮最好放在帖子编辑器而不是右上角,毕竟未在编辑帖子的时候按钮没有意义,而且移动端宽度窄也不方便布局。
重制版的其他功能与原版功能一致,样式基本都是照抄 bluecat 的和水源本身的前端。
https://shuiyuan.sjtu.edu.cn/t/topic/45454/6
基于此条反馈,除了已经有的功能独立开关以外,也许还可以对贴表情人区域提供展开/收起功能,可以切换回水源本身的样式。一种简单的样例设计:
这个“展开/收起”放在这里可能有点碍眼,然而我试过在下面工具栏加按钮会和水源本身的前端逻辑冲突导致混乱…
来源:
https://shuiyuan.sjtu.edu.cn/t/topic/45454/7
这一部分我尝试过,还有技术困难,目前的贴表情选择框点击任何区域都会导致框被关闭,也就没法输入搜索关键词…
对于图片形式的 emoji,Discourse 默认支持 7 种风格的 emoji,但似乎只有管理员才有选择权,每个用户无法选择自己喜欢的表情显示风格。取一个表情进行观察, 的链接是
https://shuiyuan.sjtu.edu.cn/images/emoji/google/cat.png?v=10
,改成 https://shuiyuan.sjtu.edu.cn/images/emoji/apple/cat.png?v=10
即可获得 Apple 风格的图片 emoji ,以此可以对图片形式的 emoji 进行风格转换。
然而涉及到原生 Unicode 字符形式的 emoji 时,转换可能存在风险:
img
元素的 title
属性。[center][size=150][code]🐸[/code][/size][/center]
这样的大号 emoji 显示效果会被破坏。这种脚本慎用哈,不说找专业人士审计,自己先至少看一遍
之前研究用于 Discourse 的浏览器插件的时候看过代码结构,应该没有问题
怎么又到限额了