Advertisement
codeGecko

[Mastodon] Auto Privacy

Aug 8th, 2022 (edited)
880
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ==UserScript==
  2. // @name            Mastodon: Auto Privacy
  3. // @namespace       Mastodon
  4. // @match           https://mastodon.art/web/*
  5. // @run-at          document-idle
  6. // @require         https://code.jquery.com/jquery-3.6.0.min.js
  7. // @grant           none
  8. // @version         1.1
  9. // @author          CBrun
  10. // @description     Set default privacy for posts and replies
  11. // ==/UserScript==
  12.  
  13.  
  14. /* --- User Settings Start --- */
  15.     //---  Privacy Defaults
  16.     /*
  17.     As of August 2022, there are 4 privacy modes identifiers for posts:
  18.         public - Public (Visible for all)
  19.         unlisted - Unlisted (Visible but opted-out of discovery)
  20.         private - Followers only
  21.         direct - Mentioned people only
  22.     */
  23.     const default_post_privacy = "direct";
  24.     const default_reply_privacy = "unlisted";
  25.  
  26.     /*
  27.     When enabled, use most restrictive privacy mode by default.
  28.         Eg: You set replies to "unlisted", but are replying a mention-only thread.
  29.         In this case the mention-only privacy mode will be used.
  30.     */
  31.     const smart_privacy = true;
  32.  
  33.     // --- CSS Identifiers used to trigger privacy change
  34.     // Don't alter in case you're not sure what you're doing
  35.     const reply_quote = "reply-indicator";      // Class of quote showing post being replied to
  36.     const privacy_dropdown = "privacy-dropdown__dropdown";      // Class of post/reply privacy options list
  37.     // The classes below must be preceded by . like regular css notations
  38.     const compose_form = ".compose-panel .compose-form";
  39.     const privacy_change_button = ".compose-panel .compose-form .privacy-dropdown button.privacy-dropdown__value-icon";
  40.     const publish_button = ".compose-panel .compose-form .compose-form__publish";       // Publish toot button
  41.     const reply_cancel_button = ".compose-panel .compose-form .reply-indicator__cancel";        // Publish toot button
  42. /* --- User Settings End --- */
  43.  
  44.  
  45. // Don't touch this!
  46. const $ = window.jQuery;
  47.  
  48. (function() {
  49.     // Initialize
  50.     let privacy_mode = false;
  51.  
  52.     let can_reset_post_privacy = false;
  53.     let can_be_smart = false;
  54.     let manual_privacy_bypass = false;
  55.  
  56.     // ===========================
  57.     // --- CONFIG
  58.     // The node to be monitored
  59.     const target = $('body')[0];
  60.  
  61.     // Observer configuration
  62.     const config = {
  63.         attributes: true,
  64.         childList: true,
  65.         characterData: false,
  66.         subtree: true
  67.     };
  68.  
  69.     // ===========================
  70.     // --- FUNCTIONS
  71.     // Set default post privacy
  72.     function setPostPrivacy( mode ) {
  73.  
  74.         $( privacy_change_button ).trigger("click", false); // Artificial click
  75.         privacy_mode = mode;    // Store temporarily for later activation
  76.     }
  77.  
  78.     // Compare privacy modes, returning strictest
  79.     function comparePrivacy( original_mode, target_mode ) {
  80.         const modes = {
  81.             "direct": 0,
  82.             "private": 1,
  83.             "unlisted": 2,
  84.             "public": 3,
  85.         }
  86.  
  87.         // Get lowest integer, then key associated to it
  88.         return getKeyByValue( modes, Math.min.apply(Math, [ modes[original_mode], modes[target_mode] ]) );
  89.     }
  90.     // Used in comparePrivacy()
  91.     function getKeyByValue( object, value ) {
  92.         return Object.keys(object).filter(key => object[key] === value);
  93.     }
  94.  
  95.     // Function to watch for composer changes and change privacy accordingly
  96.     let observer = new MutationObserver( function( mutations ) {
  97.         mutations.forEach(function( mutation ) {
  98.             let newNodes = mutation.addedNodes;     // Node addition list
  99.             let delNodes = mutation.removedNodes;   // Node removal list
  100.  
  101.             if( newNodes !== null ) {   // If nodes were added
  102.                 let $nodes = $( newNodes );     // jQuery set
  103.  
  104.                 // Check each node for a match
  105.                 $nodes.each( function() {
  106.                     let $node = $( this );
  107.                     let dropdown_is_open = $node.hasClass( privacy_dropdown );
  108.  
  109.                     // Call from smart_privacy @ reply_quote
  110.                     // if ( can_be_smart && $node.hasClass( privacy_dropdown )) {
  111.                     // User clicked on privacy dropdown, do nothing
  112.                     if ( manual_privacy_bypass ) {
  113.                         manual_privacy_bypass = false;
  114.                         return false;   // break
  115.                     }
  116.                     // Call from smart_privacy @ reply_quote
  117.                     else if ( can_be_smart && dropdown_is_open ) {
  118.  
  119.                         // Lock it so it won't called from manual clicks on the dropdown button
  120.                         can_be_smart = false;
  121.                         // Get original privacy mode
  122.                         let original_mode = $node.find('[aria-selected="true"]').attr("data-index");
  123.                         // Pick strictest one
  124.                         let stricter_mode = comparePrivacy( original_mode, privacy_mode );
  125.  
  126.                         // Set privacy mode manually, setPostPrivacy() doesn't work well here
  127.                         $node.find('[data-index="' + stricter_mode + '"]').trigger("click");
  128.                     }
  129.                     // Call from picker when posting or replying
  130.                     else if ( privacy_mode !== false && dropdown_is_open ) {
  131.  
  132.                         // Is ready to change privacy mode, do it
  133.                         $node.find('[data-index="' + privacy_mode + '"]').trigger("click");
  134.                         privacy_mode = false;   // Reset to avoid double-activation
  135.                     }
  136.                     // Change Reply privacy
  137.                     else if( $node.hasClass( reply_quote ) && dropdown_is_open === false ) {
  138.                         // Intercept and handle separately by triggering mutation
  139.                         if (smart_privacy && privacy_mode === false) {
  140.  
  141.                             $( privacy_change_button ).trigger("click", false); // Artificial click
  142.                             can_be_smart = true;    // Unlock
  143.                             privacy_mode = default_reply_privacy;   // Unlock
  144.                             return;
  145.                         }
  146.  
  147.                         setPostPrivacy( default_reply_privacy );
  148.                     }
  149.  
  150.                 });
  151.             }
  152.             // =============================================================================
  153.  
  154.             if (delNodes !== null) {    // If nodes were removed
  155.                 let $nodes = $( delNodes );
  156.  
  157.                 // Check each node for a match
  158.                 $nodes.each( function() {
  159.                     let $node = $( this );
  160.  
  161.                     // The html is ready accept a privacy reset after posting/cancelling
  162.                     if( can_reset_post_privacy ) {
  163.                         // Reset privacy to custom default
  164.                         setPostPrivacy( default_post_privacy );
  165.                         can_reset_post_privacy = false;
  166.                         return false;   // Break loop
  167.                     }
  168.  
  169.                 });
  170.             }
  171.         });
  172.     });
  173.  
  174.     // ===========================
  175.     // --- AUTO RUN
  176.     // Monitor node changes in body to handle privacy defaults
  177.     observer.observe( target, config );
  178.  
  179.     // Reset privacy to custom default after posting
  180.     $("body").on("click", publish_button + ", " + reply_cancel_button , function(e) {
  181.         // Set flag allowing default to reset when ready
  182.         can_reset_post_privacy = true;
  183.     })
  184.     .on("click", privacy_change_button , function(e, organic = true) {
  185.         // Set flag allowing default to reset when ready
  186.         manual_privacy_bypass = organic;
  187.     });
  188.  
  189.     // On page load, set initial post privacy
  190.     setPostPrivacy( default_post_privacy );
  191.  
  192. })();
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement