Merge branch 'develop'
This commit is contained in:
		
						commit
						1ce8da66c2
					
				
					 52 changed files with 965 additions and 271 deletions
				
			
		
							
								
								
									
										19
									
								
								.github/dependabot.yml
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										19
									
								
								.github/dependabot.yml
									
										
									
									
										vendored
									
									
								
							|  | @ -5,7 +5,18 @@ | ||||||
| 
 | 
 | ||||||
| version: 2 | version: 2 | ||||||
| updates: | updates: | ||||||
|   - package-ecosystem: "npm" # See documentation for possible values | - package-ecosystem: npm | ||||||
|     directory: "/" # Location of package manifests |   directory: "/" | ||||||
|     schedule: |   schedule: | ||||||
|       interval: "daily" |     interval: daily | ||||||
|  |   open-pull-requests-limit: 0 | ||||||
|  | - package-ecosystem: npm | ||||||
|  |   directory: "/packages/backend" | ||||||
|  |   schedule: | ||||||
|  |     interval: daily | ||||||
|  |   open-pull-requests-limit: 0 | ||||||
|  | - package-ecosystem: npm | ||||||
|  |   directory: "/packages/client" | ||||||
|  |   schedule: | ||||||
|  |     interval: daily | ||||||
|  |   open-pull-requests-limit: 0 | ||||||
|  |  | ||||||
|  | @ -1 +1 @@ | ||||||
| v16.6.2 | v16.13.2 | ||||||
|  |  | ||||||
							
								
								
									
										12
									
								
								CHANGELOG.md
									
										
									
									
									
								
							
							
						
						
									
										12
									
								
								CHANGELOG.md
									
										
									
									
									
								
							|  | @ -10,6 +10,18 @@ | ||||||
| You should also include the user name that made the change. | You should also include the user name that made the change. | ||||||
| --> | --> | ||||||
| 
 | 
 | ||||||
|  | ## 12.106.0 (2022/02/11) | ||||||
|  | 
 | ||||||
|  | ### Improvements | ||||||
|  | - Improve federation chart @syuilo | ||||||
|  | - クライアント: リアクションピッカーのサイズを設定できるように @syuilo | ||||||
|  | - クライアント: リアクションピッカーの幅、高さ制限を緩和 @syuilo | ||||||
|  | - Docker: Update to Node v16.13.2 @mei23 | ||||||
|  | - Update dependencies | ||||||
|  | 
 | ||||||
|  | ### Bugfixes | ||||||
|  | - validate regular expressions in word mutes @Johann150 | ||||||
|  | 
 | ||||||
| ## 12.105.0 (2022/02/09) | ## 12.105.0 (2022/02/09) | ||||||
| 
 | 
 | ||||||
| ### Improvements | ### Improvements | ||||||
|  |  | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| FROM node:16.6.2-alpine3.13 AS base | FROM node:16.13.2-alpine3.15 AS base | ||||||
| 
 | 
 | ||||||
| ENV NODE_ENV=production | ENV NODE_ENV=production | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -595,6 +595,8 @@ smtpSecure: "SMTP সংযোগের জন্য SSL/TLS ব্যবহা | ||||||
| smtpSecureInfo: "STARTTLS ব্যবহার করার সময় এটি বন্ধ করুন।" | smtpSecureInfo: "STARTTLS ব্যবহার করার সময় এটি বন্ধ করুন।" | ||||||
| testEmail: "ইমেল বিতরণ পরীক্ষা করুন" | testEmail: "ইমেল বিতরণ পরীক্ষা করুন" | ||||||
| wordMute: "বিশেষ কোন শব্দকে মিউট করুন" | wordMute: "বিশেষ কোন শব্দকে মিউট করুন" | ||||||
|  | regexpError: "রেগুলার এক্সপ্রেশন ত্রুটি" | ||||||
|  | regexpErrorDescription: "{tab} ওয়ার্ড মিউটের {line} লাইনে রেগুলার এক্সপ্রেশনে একটি ত্রুটি ছিল:" | ||||||
| instanceMute: "মিউট করা ইন্সত্যান্সগুলি" | instanceMute: "মিউট করা ইন্সত্যান্সগুলি" | ||||||
| userSaysSomething: "{name} কিছু বলেছে" | userSaysSomething: "{name} কিছু বলেছে" | ||||||
| makeActive: "সক্রিয় করা" | makeActive: "সক্রিয় করা" | ||||||
|  | @ -824,6 +826,11 @@ leaveGroupConfirm: "\"{name}\" গ্রুপ ছেড়ে চলে যেত | ||||||
| useDrawerReactionPickerForMobile: "মোবাইলে রিঅ্যাকশন পিকারকে ড্রয়ারে প্রদর্শন করুন" | useDrawerReactionPickerForMobile: "মোবাইলে রিঅ্যাকশন পিকারকে ড্রয়ারে প্রদর্শন করুন" | ||||||
| welcomeBackWithName: "আবার স্বাগতম, {name}" | welcomeBackWithName: "আবার স্বাগতম, {name}" | ||||||
| clickToFinishEmailVerification: " [{ok}] ক্লিক করার মাধ্যমে আপনার ইমেল ঠিকানা নিশ্চিত করুন।" | clickToFinishEmailVerification: " [{ok}] ক্লিক করার মাধ্যমে আপনার ইমেল ঠিকানা নিশ্চিত করুন।" | ||||||
|  | overridedDeviceKind: "ডিভাইসের ধরন" | ||||||
|  | smartphone: "স্মার্টফোন" | ||||||
|  | tablet: "ট্যাবলেট" | ||||||
|  | auto: "স্বয়ংক্রিয়" | ||||||
|  | themeColor: "থিমের রং" | ||||||
| _emailUnavailable: | _emailUnavailable: | ||||||
|   used: "এই ইমেইল ঠিকানাটি ইতোমধ্যে ব্যবহৃত হয়েছে" |   used: "এই ইমেইল ঠিকানাটি ইতোমধ্যে ব্যবহৃত হয়েছে" | ||||||
|   format: "এই ইমেল ঠিকানাটি সঠিকভাবে লিখা হয়নি" |   format: "এই ইমেল ঠিকানাটি সঠিকভাবে লিখা হয়নি" | ||||||
|  | @ -1112,10 +1119,47 @@ _2fa: | ||||||
|   registerDevice: "নতুন ডিভাইস নিবন্ধন করুন" |   registerDevice: "নতুন ডিভাইস নিবন্ধন করুন" | ||||||
|   registerKey: "সিকিউরিটি কী নিবন্ধন করুন" |   registerKey: "সিকিউরিটি কী নিবন্ধন করুন" | ||||||
|   step1: "প্রথমে, আপনার ডিভাইসে {a} বা {b} এর মতো একটি অথেনটিকেশন অ্যাপ ইনস্টল করুন৷" |   step1: "প্রথমে, আপনার ডিভাইসে {a} বা {b} এর মতো একটি অথেনটিকেশন অ্যাপ ইনস্টল করুন৷" | ||||||
|  |   step2: "এরপরে, অ্যাপের সাহায্যে প্রদর্শিত QR কোডটি স্ক্যান করুন।" | ||||||
|  |   step3: "অ্যাপে প্রদর্শিত টোকেনটি লিখুন এবং আপনার কাজ শেষ।" | ||||||
|  |   step4: "আপনাকে এখন থেকে লগ ইন করার সময়, এইভাবে টোকেন লিখতে হবে।" | ||||||
|  |   securityKeyInfo: "আপনি একটি হার্ডওয়্যার সিকিউরিটি কী ব্যবহার করে লগ ইন করতে পারেন যা FIDO2 বা ডিভাইসের ফিঙ্গারপ্রিন্ট সেন্সর বা পিন সমর্থন করে৷" | ||||||
| _permissions: | _permissions: | ||||||
|  |   "read:account": "অ্যাকাউন্টের তথ্য দেখুন" | ||||||
|  |   "write:account": "অ্যাকাউন্টের তথ্য সম্পাদন করুন" | ||||||
|  |   "read:blocks": "ব্লক করা ব্যাবহারকারীদের তালিকা দেখুন" | ||||||
|  |   "write:blocks": "ব্লক করা ব্যাবহারকারীদের তালিকা সম্পাদনা করুন" | ||||||
|  |   "read:drive": "ড্রাইভের ফাইল এবং ফোল্ডারসমূহ পড়া" | ||||||
|  |   "write:drive": "ড্রাইভের ফাইল এবং ফোল্ডারসমূহ সম্পাদনা করা" | ||||||
|  |   "read:favorites": "পছন্দের তালিকা পড়া" | ||||||
|  |   "write:favorites": "পছন্দের তালিকা সম্পাদনা করা" | ||||||
|  |   "read:following": "অনুসরণ তথ্য দেখুন" | ||||||
|  |   "write:following": "অনুসরণ তথ্য সম্পাদনা করা" | ||||||
|  |   "read:messaging": "চ্যাটগুলি দেখুন" | ||||||
|  |   "write:messaging": "চ্যাটগুলি সম্পাদনা করুন" | ||||||
|  |   "read:mutes": "মিউটের লিস্ট দেখুন" | ||||||
|  |   "write:mutes": "মিউটের লিস্ট সম্পাদনা করুন" | ||||||
|  |   "write:notes": "নোট লিখা" | ||||||
|  |   "read:notifications": "বিজ্ঞপ্তিগুলি দেখুন" | ||||||
|  |   "write:notifications": "বিজ্ঞপ্তি নিয়ে কাজ করে" | ||||||
|  |   "read:reactions": "রিঅ্যাকশনগুলি দেখুন" | ||||||
|  |   "write:reactions": "রিঅ্যাকশনগুলি সম্পাদনা করুন" | ||||||
|  |   "write:votes": "ভোট দিন" | ||||||
|   "read:pages": "আপনার পেজগুলি দেখুন" |   "read:pages": "আপনার পেজগুলি দেখুন" | ||||||
|   "write:pages": "পেজগুলি সম্পাদনা বা ডিলিট করুন" |   "write:pages": "পেজগুলি সম্পাদনা বা ডিলিট করুন" | ||||||
|  |   "read:page-likes": "পৃষ্ঠায় দেয়া পছন্দগুলি দেখুন" | ||||||
|  |   "write:page-likes": "পৃষ্ঠায় দেয়া পছন্দগুলি সম্পাদনা করুন" | ||||||
|  |   "read:user-groups": "ব্যাবহারকারী গ্রুপগুলি দেখুন" | ||||||
|  |   "write:user-groups": "ব্যাবহারকারী গ্রুপগুলি সম্পাদনা করুন" | ||||||
|  |   "read:channels": "চ্যানেলগুলি দেখুন" | ||||||
|  |   "write:channels": "চ্যানেলগুলি সম্পাদনা করুন" | ||||||
|  |   "read:gallery": "গ্যালারী দেখুন" | ||||||
|  |   "write:gallery": "গ্যালারী সম্পাদনা করুন" | ||||||
|  |   "read:gallery-likes": "গ্যালারীর পছন্দগুলি দেখুন" | ||||||
|  |   "write:gallery-likes": "গ্যালারীর পছন্দগুলি সম্পাদনা করুন" | ||||||
| _auth: | _auth: | ||||||
|  |   shareAccess: "\"{name}\" কে অ্যাকাউন্টের অ্যাক্সেস দিবেন?" | ||||||
|  |   shareAccessAsk: "অ্যাপ্লিকেশনটিকে অ্যাকাউন্টের অ্যাক্সেস দিবেন?" | ||||||
|  |   permissionAsk: "এই অ্যাপ্লিকেশনটি নিম্নলিখিত অনুমতি চাই" | ||||||
|   pleaseGoBack: "দয়া করে অ্যাপ্লিকেশনে ফিরে যান" |   pleaseGoBack: "দয়া করে অ্যাপ্লিকেশনে ফিরে যান" | ||||||
|   callback: "অ্যাপ্লিকেশনে ফিরে যাচ্ছি" |   callback: "অ্যাপ্লিকেশনে ফিরে যাচ্ছি" | ||||||
|   denied: "প্রবেশ নিষেধ" |   denied: "প্রবেশ নিষেধ" | ||||||
|  | @ -1178,48 +1222,342 @@ _poll: | ||||||
|   closed: "শেষ হয়ে গেছে" |   closed: "শেষ হয়ে গেছে" | ||||||
|   remainingDays: "আর {d} দিন {h} ঘণ্টা বাকি আছে" |   remainingDays: "আর {d} দিন {h} ঘণ্টা বাকি আছে" | ||||||
|   remainingHours: "আর {h} ঘণ্টা {m} মিনিট বাকি আছে" |   remainingHours: "আর {h} ঘণ্টা {m} মিনিট বাকি আছে" | ||||||
|  |   remainingMinutes: "আর বাকি আছে {m} মিনিট {s} সেকেন্ড" | ||||||
|  |   remainingSeconds: "আর বাকি আছে {s} সেকেন্ড" | ||||||
| _visibility: | _visibility: | ||||||
|  |   public: "সর্বজনীন" | ||||||
|  |   publicDescription: "সবাই আপনার নোটগুলি দেখতে পাবে" | ||||||
|   home: "মূল পাতা" |   home: "মূল পাতা" | ||||||
|  |   homeDescription: "শুধুমাত্র হোম টাইমলাইনে আপনার নোটগুলি পোস্ট করুন" | ||||||
|   followers: "অনুসরণকারী" |   followers: "অনুসরণকারী" | ||||||
|  |   followersDescription: "শুধুমাত্র আপনার অনুসরণকারীদের নিকট পোস্ট করুন" | ||||||
|  |   specified: "ডাইরেক্ট নোট" | ||||||
|  |   specifiedDescription: "শুধুমাত্র নির্দিষ্ট ব্যাবহারকারীর নিকট পাঠান" | ||||||
|  |   localOnly: "শুধুমাত্র লোকাল" | ||||||
|  |   localOnlyDescription: "রিমোট ব্যাবহারকারীদের নিকট দৃশ্যমান নয়" | ||||||
|  | _postForm: | ||||||
|  |   replyPlaceholder: "নোটটির জবাব দিন..." | ||||||
|  |   quotePlaceholder: "নোটটিকে উদ্ধৃত করুন..." | ||||||
|  |   channelPlaceholder: "চ্যানেলে পোস্ট করুন..." | ||||||
|  |   _placeholders: | ||||||
|  |     a: "আপনি এখন কি করছেন?" | ||||||
|  |     b: "আপনার আশে পাশে কি হচ্ছে?" | ||||||
|  |     c: "আপনি কি ভাবছেন?" | ||||||
|  |     d: "আপনি কি বলতে চান?" | ||||||
|  |     e: "লেখা শুরু করুন..." | ||||||
|  |     f: "আপনার লেখার জন্য অপেক্ষা করছি..." | ||||||
| _profile: | _profile: | ||||||
|   name: "নাম" |   name: "নাম" | ||||||
|   username: "ব্যবহারকারীর নাম" |   username: "ব্যবহারকারীর নাম" | ||||||
|  |   description: "আপনার সম্পর্কে" | ||||||
|  |   youCanIncludeHashtags: "হ্যাশট্যাগ অন্তর্ভুক্ত করা যেতে পারে।" | ||||||
|  |   metadata: "অতিরিক্ত তথ্য" | ||||||
|  |   metadataEdit: "অতিরিক্ত তথ্য সম্পাদনা করুন" | ||||||
|  |   metadataDescription: "আপনি আপনার প্রোফাইলে একটি টেবিল হিসাবে চারটি অতিরিক্ত তথ্য দেখাতে পারেন।" | ||||||
|  |   metadataLabel: "লেবেল" | ||||||
|  |   metadataContent: "বিষয়বস্তু" | ||||||
|  |   changeAvatar: "অ্যাভাটার পরিবর্তন করুন" | ||||||
|  |   changeBanner: "ব্যানার পরিবর্তন করুন" | ||||||
| _exportOrImport: | _exportOrImport: | ||||||
|   allNotes: "সকল নোট" |   allNotes: "সকল নোট" | ||||||
|   followingList: "অনুসরণ করা হচ্ছে" |   followingList: "অনুসরণ করা হচ্ছে" | ||||||
|   muteList: "মিউট" |   muteList: "মিউট" | ||||||
|   blockingList: "ব্লক" |   blockingList: "ব্লক" | ||||||
|   userLists: "লিস্ট" |   userLists: "লিস্ট" | ||||||
|  |   excludeMutingUsers: "মিউটকৃত ব্যবহারকারীদের বাদ দিন" | ||||||
|  |   excludeInactiveUsers: "অব্যাবহৃত অ্যাকাউন্ট বাদ দিন" | ||||||
| _charts: | _charts: | ||||||
|   federation: "ফেডিভার্স" |   federation: "ফেডিভার্স" | ||||||
|  |   apRequest: "অনুরোধসমূহ" | ||||||
|  |   usersIncDec: "ব্যবহারকারীদের সংখ্যার পরিবর্তন" | ||||||
|  |   usersTotal: "ব্যবহারকারীদের সংখ্যা" | ||||||
|  |   activeUsers: "সক্রিয় ব্যাবহারকারী" | ||||||
|  |   notesIncDec: "নোটের সংখ্যার পরিবর্তন" | ||||||
|  |   localNotesIncDec: "লোকাল নোটের সংখ্যার পরিবর্তন" | ||||||
|  |   remoteNotesIncDec: "রিমোট নোটের সংখ্যার পরিবর্তন" | ||||||
|  |   notesTotal: "নোটের সংখ্যা" | ||||||
|  |   filesIncDec: "ফাইলের সংখ্যার পরিবর্তন" | ||||||
|  |   filesTotal: "ফাইলের সংখ্যা" | ||||||
|  |   storageUsageIncDec: "স্টোরেজের ব্যাবহারের পরিবর্তন" | ||||||
|  |   storageUsageTotal: "মোট স্টোরেজের ব্যাবহার" | ||||||
|  | _instanceCharts: | ||||||
|  |   requests: "অনুরোধসমূহ" | ||||||
|  |   users: "ব্যবহারকারীদের সংখ্যার পরিবর্তন" | ||||||
|  |   usersTotal: "ক্রমবর্ধমান ব্যবহারকারীদের সংখ্যা" | ||||||
|  |   notes: "নোটের সংখ্যার পরিবর্তন" | ||||||
|  |   notesTotal: "ক্রমবর্ধমান নোটের সংখ্যা" | ||||||
|  |   ff: "অনুসরণকারী / অনুসরণ করা ব্যাবহারকারীদের সংখ্যার পরিবর্তন" | ||||||
|  |   ffTotal: "অনুসরণকারী / অনুসরণ করা ব্যাবহারকারীদের ক্রমবর্ধমান সংখ্যা" | ||||||
|  |   cacheSize: "ক্যাশ সাইজের পরিবর্তন" | ||||||
|  |   cacheSizeTotal: "ক্রমবর্ধমান ক্যাশ সাইজ" | ||||||
|  |   files: "ফাইলের সংখ্যার পরিবর্তন" | ||||||
|  |   filesTotal: "ক্রমবর্ধমান ফাইলের সংখ্যা" | ||||||
| _timelines: | _timelines: | ||||||
|   home: "মূল পাতা" |   home: "মূল পাতা" | ||||||
|  |   local: "স্থানীয়" | ||||||
|  |   social: "সামাজিক" | ||||||
|  |   global: "গ্লোবাল" | ||||||
| _pages: | _pages: | ||||||
|  |   newPage: "নতুন পৃষ্ঠা বানান" | ||||||
|  |   editPage: "পৃষ্ঠাটি সম্পাদনা করুন" | ||||||
|  |   readPage: "উৎস দেখছেন" | ||||||
|  |   created: "পৃষ্ঠা তৈরি করা হয়েছে" | ||||||
|  |   updated: "পৃষ্ঠা সম্পাদনা করা হয়েছে" | ||||||
|  |   deleted: "পৃষ্ঠা মুছে ফেলা হয়েছে" | ||||||
|  |   pageSetting: "পৃষ্ঠার সেটিংস" | ||||||
|  |   nameAlreadyExists: "পৃষ্ঠার URLটি ইতিমধ্যেই ব্যাবহার করা হয়েছে" | ||||||
|  |   invalidNameTitle: "পৃষ্ঠার URL অবৈধ" | ||||||
|  |   invalidNameText: "নিশ্চিত করুন যে এটি ফাঁকা নয়" | ||||||
|  |   editThisPage: "পৃষ্ঠাটি সম্পাদনা করুন" | ||||||
|  |   viewSource: "উৎস দেখুন" | ||||||
|   viewPage: "আপনার পেজগুলি দেখুন" |   viewPage: "আপনার পেজগুলি দেখুন" | ||||||
|  |   like: "পছন্দ" | ||||||
|  |   unlike: "পছন্দ সরান" | ||||||
|  |   my: "আমার পৃষ্ঠাগুলি" | ||||||
|  |   liked: "পছন্দ করা পৃষ্ঠাগুলি" | ||||||
|  |   featured: "জনপ্রিয়" | ||||||
|  |   inspector: "ইনিস্পেক্টর" | ||||||
|  |   contents: "বিষয়বস্তু" | ||||||
|  |   content: "পৃষ্ঠার ব্লক" | ||||||
|  |   variables: "চলকগুলি" | ||||||
|  |   title: "শিরোনাম" | ||||||
|  |   url: "পৃষ্ঠার URL" | ||||||
|  |   summary: "পৃষ্ঠার বর্ণনা" | ||||||
|  |   alignCenter: "সেন্টার" | ||||||
|  |   hideTitleWhenPinned: "পিন করা হলে টাইটেল লুকান" | ||||||
|  |   font: "ফন্ট" | ||||||
|  |   fontSerif: "সেরিফ" | ||||||
|  |   fontSansSerif: "স্যান্স সেরিফ" | ||||||
|  |   eyeCatchingImageSet: "থাম্বনেইল সেট করুন" | ||||||
|  |   eyeCatchingImageRemove: "থাম্বনেইল সরান" | ||||||
|  |   chooseBlock: "ব্লক যোগ করুন" | ||||||
|  |   selectType: "ধরন নির্বাচন করুন" | ||||||
|  |   enterVariableName: "চলকের নাম লিখুন" | ||||||
|  |   variableNameIsAlreadyUsed: "চলকের নামটি ইতিপূর্বে ব্যাবহৃত হয়েছে" | ||||||
|  |   contentBlocks: "বিষয়বস্তু" | ||||||
|  |   inputBlocks: "ইনপুট" | ||||||
|  |   specialBlocks: "বিশেষ" | ||||||
|   blocks: |   blocks: | ||||||
|  |     text: "লেখা" | ||||||
|  |     textarea: "টেক্সট এরিয়া" | ||||||
|  |     section: "বিভাগ" | ||||||
|     image: "ছবি" |     image: "ছবি" | ||||||
|  |     button: "বাটন" | ||||||
|  |     if: "যদি" | ||||||
|  |     _if: | ||||||
|  |       variable: "চলকগুলি" | ||||||
|  |     post: "নোট লিখুন" | ||||||
|  |     _post: | ||||||
|  |       text: "বিষয়বস্তু" | ||||||
|  |       attachCanvasImage: "ক্যানভাস ছবিসহ পোস্ট করুন" | ||||||
|  |       canvasId: "ক্যানভাস ID" | ||||||
|  |     textInput: "টেক্সট ইনপুট" | ||||||
|  |     _textInput: | ||||||
|  |       name: "চলকের নাম" | ||||||
|  |       text: "শিরোনাম" | ||||||
|  |       default: "ডিফল্ট মান" | ||||||
|  |     textareaInput: "একাধিক লাইনের টেক্সট ইনপুট" | ||||||
|  |     _textareaInput: | ||||||
|  |       name: "চলকের নাম" | ||||||
|  |       text: "শিরোনাম" | ||||||
|  |       default: "ডিফল্ট মান" | ||||||
|  |     numberInput: "সংখ্যা ইনপুট" | ||||||
|  |     _numberInput: | ||||||
|  |       name: "চলকের নাম" | ||||||
|  |       text: "শিরোনাম" | ||||||
|  |       default: "ডিফল্ট মান" | ||||||
|  |     canvas: "ক্যানভাস" | ||||||
|  |     _canvas: | ||||||
|  |       id: "ক্যানভাস ID" | ||||||
|  |       width: "প্রস্থ" | ||||||
|  |       height: "উচ্চতা" | ||||||
|  |     note: "এম্বেড নোট" | ||||||
|  |     _note: | ||||||
|  |       id: "নোট ID" | ||||||
|  |       idDescription: "আপনি এর বদলে নোটের URL পেস্ট করতে পারেন." | ||||||
|  |       detailed: "বিস্তারিত দেখুন" | ||||||
|  |     switch: "সুইচ" | ||||||
|  |     _switch: | ||||||
|  |       name: "চলকের নাম" | ||||||
|  |       text: "শিরোনাম" | ||||||
|  |       default: "ডিফল্ট মান" | ||||||
|  |     counter: "কাউন্টার" | ||||||
|  |     _counter: | ||||||
|  |       name: "চলকের নাম" | ||||||
|  |       text: "শিরোনাম" | ||||||
|  |       inc: "এভাবে মান বাড়ান" | ||||||
|  |     _button: | ||||||
|  |       text: "শিরোনাম" | ||||||
|  |       colored: "রঙ্গিন" | ||||||
|  |       action: "বাটনে ক্লিক করলে যা হবে" | ||||||
|  |       _action: | ||||||
|  |         dialog: "ডায়ালগ দেখান " | ||||||
|  |         _dialog: | ||||||
|  |           content: "বিষয়বস্তু" | ||||||
|  |         resetRandom: "র্যানডম সিড রিসেট করুন" | ||||||
|  |         pushEvent: "ইভেন্ট পাঠান" | ||||||
|  |         _pushEvent: | ||||||
|  |           event: "ইভেন্টের নাম" | ||||||
|  |           message: "চালু হলে প্রদর্শনের জন্য বার্তা" | ||||||
|  |           variable: "পাঠানো চলক" | ||||||
|  |           no-variable: "কিছুই না" | ||||||
|  |         callAiScript: "AiScript চালান" | ||||||
|  |         _callAiScript: | ||||||
|  |           functionName: "ফাংশনের নাম" | ||||||
|  |     radioButton: "বহুনির্বাচনী" | ||||||
|  |     _radioButton: | ||||||
|  |       name: "চলকের নাম" | ||||||
|  |       title: "শিরোনাম" | ||||||
|  |       values: "বিকল্পগুলিকে আলাদা লাইনে লিখুন" | ||||||
|  |       default: "ডিফল্ট মান" | ||||||
|   script: |   script: | ||||||
|     categories: |     categories: | ||||||
|  |       flow: "নিয়ন্ত্রণ" | ||||||
|  |       logical: "লজিক্যাল অপারেশন" | ||||||
|  |       operation: "হিসাব-নিকাশ" | ||||||
|  |       comparison: "তুলনা" | ||||||
|  |       random: "র্যান্ডম" | ||||||
|  |       value: "মান" | ||||||
|  |       fn: "ফাংশন" | ||||||
|  |       text: "টেক্সট ম্যানিপুলেশন" | ||||||
|  |       convert: "রুপান্তর" | ||||||
|       list: "লিস্ট" |       list: "লিস্ট" | ||||||
|     blocks: |     blocks: | ||||||
|  |       text: "লেখা" | ||||||
|  |       multiLineText: "লেখা (একাধিক লাইন)" | ||||||
|  |       textList: "লেখার লিস্ট" | ||||||
|  |       _textList: | ||||||
|  |         info: "প্রতিটি এন্ট্রিকে আলাদা লাইনে লিখুন" | ||||||
|  |       strLen: "লেখার দৈর্ঘ্য" | ||||||
|  |       _strLen: | ||||||
|  |         arg1: "লেখা" | ||||||
|  |       strPick: "অক্ষর বের করে আনুন" | ||||||
|  |       _strPick: | ||||||
|  |         arg1: "লেখা" | ||||||
|  |         arg2: "অক্ষরের অবস্থান" | ||||||
|  |       strReplace: "লেখা প্রতিস্থাপন" | ||||||
|  |       _strReplace: | ||||||
|  |         arg1: "লেখা" | ||||||
|  |         arg2: "যে লেখা প্রতিস্থাপন করা হবে" | ||||||
|  |         arg3: "যা দ্বারা প্রতিস্থাপন করা হবে" | ||||||
|  |       strReverse: "লেখা উল্টান" | ||||||
|  |       _strReverse: | ||||||
|  |         arg1: "লেখা" | ||||||
|  |       join: "লেখা যুক্ত করুন" | ||||||
|       _join: |       _join: | ||||||
|         arg1: "লিস্ট" |         arg1: "লিস্ট" | ||||||
|  |         arg2: "বিভাজক" | ||||||
|  |       add: "যোগ" | ||||||
|  |       _add: | ||||||
|  |         arg1: "A" | ||||||
|  |         arg2: "B" | ||||||
|  |       subtract: "বিয়োগ" | ||||||
|  |       _subtract: | ||||||
|  |         arg1: "A" | ||||||
|  |         arg2: "B" | ||||||
|  |       multiply: "গুন" | ||||||
|  |       _multiply: | ||||||
|  |         arg1: "A" | ||||||
|  |         arg2: "B" | ||||||
|  |       divide: "ভাগ" | ||||||
|  |       _divide: | ||||||
|  |         arg1: "A" | ||||||
|  |         arg2: "B" | ||||||
|  |       mod: "ভাগশেষ" | ||||||
|  |       _mod: | ||||||
|  |         arg1: "A" | ||||||
|  |         arg2: "B" | ||||||
|  |       round: "দশমিক রাউন্ড করুন" | ||||||
|  |       _round: | ||||||
|  |         arg1: "সংখ্যা" | ||||||
|  |       eq: "A ও B সমান" | ||||||
|  |       _eq: | ||||||
|  |         arg1: "A" | ||||||
|  |         arg2: "B" | ||||||
|  |       notEq: "A ও B সমান না" | ||||||
|  |       _notEq: | ||||||
|  |         arg1: "A" | ||||||
|  |         arg2: "B" | ||||||
|  |       and: "A এবং B" | ||||||
|  |       _and: | ||||||
|  |         arg1: "A" | ||||||
|  |         arg2: "B" | ||||||
|  |       or: "A অথবা B" | ||||||
|  |       _or: | ||||||
|  |         arg1: "A" | ||||||
|  |         arg2: "B" | ||||||
|  |       lt: "< A , B হতে কম" | ||||||
|  |       _lt: | ||||||
|  |         arg1: "A" | ||||||
|  |         arg2: "B" | ||||||
|  |       gt: "> A , B হতে বেশী" | ||||||
|  |       _gt: | ||||||
|  |         arg1: "A" | ||||||
|  |         arg2: "B" | ||||||
|  |       ltEq: "<= A , B হতে কম বা সমান" | ||||||
|  |       _ltEq: | ||||||
|  |         arg1: "A" | ||||||
|  |         arg2: "B" | ||||||
|  |       gtEq: ">= A , B হতে বেশী বা সমান" | ||||||
|  |       _gtEq: | ||||||
|  |         arg1: "A" | ||||||
|  |         arg2: "B" | ||||||
|  |       if: "যদি" | ||||||
|  |       _if: | ||||||
|  |         arg1: "যদি" | ||||||
|  |         arg2: "তাহলে" | ||||||
|  |         arg3: "তাছাড়া" | ||||||
|  |       not: "না" | ||||||
|  |       _not: | ||||||
|  |         arg1: "না" | ||||||
|  |       random: "র্যান্ডম" | ||||||
|  |       _random: | ||||||
|  |         arg1: "সম্ভাব্যতা" | ||||||
|  |       rannum: "র্যানডম সংখ্যা" | ||||||
|  |       _rannum: | ||||||
|  |         arg1: "ন্যূনতম মান" | ||||||
|  |         arg2: "সর্বোচ্চ মান" | ||||||
|  |       randomPick: "তালিকা থেকে দৈবচয়ন করুন" | ||||||
|       _randomPick: |       _randomPick: | ||||||
|         arg1: "লিস্ট" |         arg1: "লিস্ট" | ||||||
|  |       dailyRandom: "র্যান্ডম সংখ্যা (প্রতিটি ব্যবহারকারীর জন্য প্রতিদিন পরিবর্তীত হয়)" | ||||||
|       _dailyRandom: |       _dailyRandom: | ||||||
|         arg1: "সম্ভাব্যতা" |         arg1: "সম্ভাব্যতা" | ||||||
|       dailyRannum: "র্যান্ডম সংখ্যা (প্রতিটি ব্যবহারকারীর জন্য প্রতিদিন পরিবর্তীত হয়)" |       dailyRannum: "র্যান্ডম সংখ্যা (প্রতিটি ব্যবহারকারীর জন্য প্রতিদিন পরিবর্তীত হয়)" | ||||||
|       _dailyRannum: |       _dailyRannum: | ||||||
|         arg1: "ন্যূনতম মান" |         arg1: "ন্যূনতম মান" | ||||||
|         arg2: "সর্বোচ্চ মান" |         arg2: "সর্বোচ্চ মান" | ||||||
|  |       dailyRandomPick: "তালিকা থেকে এলোমেলোভাবে নির্বাচন করুন (প্রতিটি ব্যবহারকারীর জন্য প্রতিদিন পরিবর্তীত হয়)" | ||||||
|       _dailyRandomPick: |       _dailyRandomPick: | ||||||
|         arg1: "লিস্ট" |         arg1: "লিস্ট" | ||||||
|  |       seedRandom: "র্যানডম (সীড দ্বারা)" | ||||||
|  |       _seedRandom: | ||||||
|  |         arg1: "সীড" | ||||||
|  |         arg2: "সম্ভাব্যতা" | ||||||
|  |       seedRannum: "র্যানডম সংখ্যা (সীড দ্বারা)" | ||||||
|  |       _seedRannum: | ||||||
|  |         arg1: "সীড" | ||||||
|  |         arg2: "ন্যূনতম মান" | ||||||
|  |         arg3: "সর্বোচ্চ মান" | ||||||
|  |       seedRandomPick: "তালিকা থেকে দৈবচয়ন করুন (সীড দ্বারা)" | ||||||
|       _seedRandomPick: |       _seedRandomPick: | ||||||
|  |         arg1: "সীড" | ||||||
|         arg2: "লিস্ট" |         arg2: "লিস্ট" | ||||||
|  |       DRPWPM: "সম্ভাব্যতা সহ একটি তালিকা থেকে এলোমেলোভাবে নির্বাচন করুন (প্রতিটি ব্যবহারকারীর জন্য প্রতিদিন)" | ||||||
|  |       _DRPWPM: | ||||||
|  |         arg1: "লেখার লিস্ট" | ||||||
|  |       pick: "তালিকা থেকে নির্বাচন করুন" | ||||||
|       _pick: |       _pick: | ||||||
|         arg1: "লিস্ট" |         arg1: "লিস্ট" | ||||||
|  |         arg2: "অবস্থান" | ||||||
|  |       listLen: "লিস্টের দৈর্ঘ্য পান" | ||||||
|       _listLen: |       _listLen: | ||||||
|         arg1: "লিস্ট" |         arg1: "লিস্ট" | ||||||
|  |       number: "সংখ্যা" | ||||||
|  |       stringToNumber: "পাঠ্য থেকে সংখ্যা" | ||||||
|  |       _stringToNumber: | ||||||
|  |         arg1: "লেখা" | ||||||
|  |       numberToString: "সংখ্যা থেকে পাঠ্য" | ||||||
|       _numberToString: |       _numberToString: | ||||||
|         arg1: "সংখ্যা" |         arg1: "সংখ্যা" | ||||||
|       splitStrByLine: "পাঠ্যকে লাইনে বিভক্ত করুন" |       splitStrByLine: "পাঠ্যকে লাইনে বিভক্ত করুন" | ||||||
|  |  | ||||||
|  | @ -595,6 +595,8 @@ smtpSecure: "Für SMTP-Verbindungen implizit SSL/TLS verwenden" | ||||||
| smtpSecureInfo: "Schalte dies aus, falls du STARTTLS verwendest" | smtpSecureInfo: "Schalte dies aus, falls du STARTTLS verwendest" | ||||||
| testEmail: "Email-Versand testen" | testEmail: "Email-Versand testen" | ||||||
| wordMute: "Wort-Stummschaltung" | wordMute: "Wort-Stummschaltung" | ||||||
|  | regexpError: "Regular Expression error" | ||||||
|  | regexpErrorDescription: "Error in the regular expression on line {line} in your {tab} word mutes:" | ||||||
| instanceMute: "Instanzstummschaltungen" | instanceMute: "Instanzstummschaltungen" | ||||||
| userSaysSomething: "{name} hat etwas gesagt" | userSaysSomething: "{name} hat etwas gesagt" | ||||||
| makeActive: "Aktivieren" | makeActive: "Aktivieren" | ||||||
|  | @ -828,6 +830,7 @@ overridedDeviceKind: "Gerätetyp" | ||||||
| smartphone: "Smartphone" | smartphone: "Smartphone" | ||||||
| tablet: "Tablet" | tablet: "Tablet" | ||||||
| auto: "Automatisch" | auto: "Automatisch" | ||||||
|  | themeColor: "Instanzfarbe" | ||||||
| _emailUnavailable: | _emailUnavailable: | ||||||
|   used: "Diese Email-Adresse wird bereits verwendet" |   used: "Diese Email-Adresse wird bereits verwendet" | ||||||
|   format: "Das Format dieser Email-Adresse ist ungültig" |   format: "Das Format dieser Email-Adresse ist ungültig" | ||||||
|  |  | ||||||
|  | @ -828,6 +828,7 @@ overridedDeviceKind: "Device type" | ||||||
| smartphone: "Smartphone" | smartphone: "Smartphone" | ||||||
| tablet: "Tablet" | tablet: "Tablet" | ||||||
| auto: "Auto" | auto: "Auto" | ||||||
|  | themeColor: "Theme Color" | ||||||
| _emailUnavailable: | _emailUnavailable: | ||||||
|   used: "This email address is already being used" |   used: "This email address is already being used" | ||||||
|   format: "The format of this email address is invalid" |   format: "The format of this email address is invalid" | ||||||
|  |  | ||||||
|  | @ -9,7 +9,7 @@ username: "Uzantnomo" | ||||||
| password: "Pasvorto" | password: "Pasvorto" | ||||||
| forgotPassword: "Ĉu vi forgesis pasvorton?" | forgotPassword: "Ĉu vi forgesis pasvorton?" | ||||||
| fetchingAsApObject: "Informpetado de la Fediverso…" | fetchingAsApObject: "Informpetado de la Fediverso…" | ||||||
| ok: "OK" | ok: "Bone" | ||||||
| gotIt: "Kompreni" | gotIt: "Kompreni" | ||||||
| cancel: "Nuligi" | cancel: "Nuligi" | ||||||
| enterUsername: "Entajpu uzantnomon" | enterUsername: "Entajpu uzantnomon" | ||||||
|  | @ -75,7 +75,7 @@ following: "Sekvata" | ||||||
| followers: "Sekvantoj" | followers: "Sekvantoj" | ||||||
| followsYou: "Sekvas vin" | followsYou: "Sekvas vin" | ||||||
| createList: "Krei liston" | createList: "Krei liston" | ||||||
| manageLists: "Bonteni liston" | manageLists: "Bonteni la listojn" | ||||||
| error: "Eraro" | error: "Eraro" | ||||||
| somethingHappened: "Problemo okazis" | somethingHappened: "Problemo okazis" | ||||||
| retry: "Provi denove" | retry: "Provi denove" | ||||||
|  | @ -137,6 +137,7 @@ settingGuide: "Agordaj rekomendoj" | ||||||
| cacheRemoteFiles: "Stapli forajn dosierojn" | cacheRemoteFiles: "Stapli forajn dosierojn" | ||||||
| flagAsBot: "Marki kiel esti uzanto de roboto" | flagAsBot: "Marki kiel esti uzanto de roboto" | ||||||
| flagAsCat: "Marki kiel esti kato" | flagAsCat: "Marki kiel esti kato" | ||||||
|  | flagAsCatDescription: "Flagu por montri ke la konton havas kato." | ||||||
| flagShowTimelineReplies: "Montri respondon de notoj en templinio." | flagShowTimelineReplies: "Montri respondon de notoj en templinio." | ||||||
| autoAcceptFollowed: "Aŭtomate akcepti la peton de sekvado far uzantoj kiujn vi sekvas" | autoAcceptFollowed: "Aŭtomate akcepti la peton de sekvado far uzantoj kiujn vi sekvas" | ||||||
| addAccount: "Aldoni konton" | addAccount: "Aldoni konton" | ||||||
|  | @ -184,7 +185,7 @@ noUsers: "Neniu uzanto" | ||||||
| editProfile: "Redakti profilon" | editProfile: "Redakti profilon" | ||||||
| noteDeleteConfirm: "Ĉu vi certas ke vi volas forviŝi la noton?" | noteDeleteConfirm: "Ĉu vi certas ke vi volas forviŝi la noton?" | ||||||
| pinLimitExceeded: "Vi ne povas alpingli pli" | pinLimitExceeded: "Vi ne povas alpingli pli" | ||||||
| intro: "Instalado de Misskey finiĝis! Kreu administran konton." | intro: "La instalado de Misskey finiĝis! Volu krei administran konton." | ||||||
| done: "Fini" | done: "Fini" | ||||||
| processing: "Prilaborado…" | processing: "Prilaborado…" | ||||||
| preview: "Antaŭmontro" | preview: "Antaŭmontro" | ||||||
|  | @ -223,6 +224,7 @@ resetAreYouSure: "Ĉu vi certas restarigi?" | ||||||
| saved: "Konservita" | saved: "Konservita" | ||||||
| messaging: "Retbabili" | messaging: "Retbabili" | ||||||
| upload: "Alŝuti" | upload: "Alŝuti" | ||||||
|  | keepOriginalUploading: "Konservi la originalan bildon" | ||||||
| fromDrive: "De la disko" | fromDrive: "De la disko" | ||||||
| fromUrl: "De URL" | fromUrl: "De URL" | ||||||
| uploadFromUrl: "Alŝuti de URL" | uploadFromUrl: "Alŝuti de URL" | ||||||
|  | @ -491,7 +493,7 @@ deletedNote: "Forviŝita noto" | ||||||
| invisibleNote: "Malpublikigita noto" | invisibleNote: "Malpublikigita noto" | ||||||
| enableInfiniteScroll: "Ebligi infinitan rulumon" | enableInfiniteScroll: "Ebligi infinitan rulumon" | ||||||
| visibility: "Videbleco" | visibility: "Videbleco" | ||||||
| poll: "Balotujo" | poll: "Enketo" | ||||||
| useCw: "Kaŝi enhavo" | useCw: "Kaŝi enhavo" | ||||||
| enablePlayer: "Vidigi la filmeton" | enablePlayer: "Vidigi la filmeton" | ||||||
| disablePlayer: "Malfermi la filmeton" | disablePlayer: "Malfermi la filmeton" | ||||||
|  | @ -537,7 +539,7 @@ overview: "Resumo" | ||||||
| logs: "Protokoloj" | logs: "Protokoloj" | ||||||
| delayed: "Prokrasto " | delayed: "Prokrasto " | ||||||
| database: "Datumbazo" | database: "Datumbazo" | ||||||
| channel: "Kanalo" | channel: "Kanaloj" | ||||||
| create: "Krei" | create: "Krei" | ||||||
| notificationSetting: "Agordoj de sciigoj" | notificationSetting: "Agordoj de sciigoj" | ||||||
| useGlobalSetting: "Oni uzas malloka agordo" | useGlobalSetting: "Oni uzas malloka agordo" | ||||||
|  | @ -560,6 +562,7 @@ createNew: "Krei novan" | ||||||
| optional: "Opciaj" | optional: "Opciaj" | ||||||
| public: "Publika" | public: "Publika" | ||||||
| i18nInfo: "Misskey estas tradukata en diversaj lingvoj de volontuloj. Oni povas kontribui ĉe {link}." | i18nInfo: "Misskey estas tradukata en diversaj lingvoj de volontuloj. Oni povas kontribui ĉe {link}." | ||||||
|  | manageAccessTokens: "Bonteni la aŭtentikigajn pecojn" | ||||||
| accountInfo: "Kontaj Informoj" | accountInfo: "Kontaj Informoj" | ||||||
| notesCount: "La nombro de notoj" | notesCount: "La nombro de notoj" | ||||||
| repliesCount: "La nombro de respondoj senditaj" | repliesCount: "La nombro de respondoj senditaj" | ||||||
|  | @ -571,6 +574,7 @@ followersCount: "La nombro de sekvantoj" | ||||||
| sentReactionsCount: "La nombro de la reagoj senditaj" | sentReactionsCount: "La nombro de la reagoj senditaj" | ||||||
| receivedReactionsCount: "La nombro de la reagoj ricevitaj" | receivedReactionsCount: "La nombro de la reagoj ricevitaj" | ||||||
| pollVotesCount: "Nombro de voĉdonado" | pollVotesCount: "Nombro de voĉdonado" | ||||||
|  | pollVotedCount: "La nombro de la voĉoj ricevitaj en siaj enketoj" | ||||||
| yes: "Jes" | yes: "Jes" | ||||||
| no: "Ne" | no: "Ne" | ||||||
| driveFilesCount: "La nombro de la dosieroj sur la disko" | driveFilesCount: "La nombro de la dosieroj sur la disko" | ||||||
|  | @ -615,7 +619,7 @@ inUse: "Uzata" | ||||||
| editCode: "Redakti kodon" | editCode: "Redakti kodon" | ||||||
| receiveAnnouncementFromInstance: "Ricevi informojn sciigintajn de la nodo" | receiveAnnouncementFromInstance: "Ricevi informojn sciigintajn de la nodo" | ||||||
| emailNotification: "Sciigoj per retpoŝto" | emailNotification: "Sciigoj per retpoŝto" | ||||||
| inChannelSearch: "Serĉi en kanalo" | inChannelSearch: "Serĉi en la kanalo" | ||||||
| useReactionPickerForContextMenu: "Dekstre-klaki por malfermi la elektilon de reagoj" | useReactionPickerForContextMenu: "Dekstre-klaki por malfermi la elektilon de reagoj" | ||||||
| typingUsers: "{users} nun skribas…" | typingUsers: "{users} nun skribas…" | ||||||
| clear: "Vakigi" | clear: "Vakigi" | ||||||
|  | @ -635,7 +639,7 @@ administration: "Bontenado" | ||||||
| accounts: "Kontoj" | accounts: "Kontoj" | ||||||
| configure: "Agordi" | configure: "Agordi" | ||||||
| recentPosts: "Novaj afiŝoj" | recentPosts: "Novaj afiŝoj" | ||||||
| popularPosts: "Populara noto" | popularPosts: "Plej viditaj" | ||||||
| shareWithNote: "Kundividi en noto" | shareWithNote: "Kundividi en noto" | ||||||
| ads: "Reklamaĵo" | ads: "Reklamaĵo" | ||||||
| expiration: "Limtempo" | expiration: "Limtempo" | ||||||
|  | @ -646,6 +650,7 @@ low: "Malalta" | ||||||
| emailNotConfiguredWarning: "Vi ne agordis retpoŝtadreso." | emailNotConfiguredWarning: "Vi ne agordis retpoŝtadreso." | ||||||
| customCss: "Personecigita CSS" | customCss: "Personecigita CSS" | ||||||
| global: "Malloka" | global: "Malloka" | ||||||
|  | squareAvatars: "Montri bildsimbolon kiel kvadrata" | ||||||
| sent: "Sendi" | sent: "Sendi" | ||||||
| received: "Ricevita" | received: "Ricevita" | ||||||
| searchResult: "Serĉorezultoj" | searchResult: "Serĉorezultoj" | ||||||
|  | @ -674,10 +679,14 @@ ffVisibilityDescription: "Oni permesas agordi tiuln kiuj povas vidi la homojn ki | ||||||
| continueThread: "Pli vidi la mesaĝaron" | continueThread: "Pli vidi la mesaĝaron" | ||||||
| incorrectPassword: "Nevalida pasvorto" | incorrectPassword: "Nevalida pasvorto" | ||||||
| voteConfirm: "Ĉu vi voĉdonas {choice}n?" | voteConfirm: "Ĉu vi voĉdonas {choice}n?" | ||||||
|  | hide: "Kaŝi" | ||||||
| leaveGroup: "Eliĝi el la grupo" | leaveGroup: "Eliĝi el la grupo" | ||||||
| leaveGroupConfirm: "Ĉu vi certas ke vi volas eliĝi el la grupo {name}?" | leaveGroupConfirm: "Ĉu vi certas ke vi volas eliĝi el la grupo {name}?" | ||||||
| welcomeBackWithName: "Bonrevenon, {name}!" | welcomeBackWithName: "Bonrevenon, {name}!" | ||||||
| clickToFinishEmailVerification: "Volu klaki [{ok}] por fini la konfirmon de vian retadreson" | clickToFinishEmailVerification: "Volu klaki [{ok}] por fini la konfirmon de vian retadreson" | ||||||
|  | smartphone: "Saĝtelefono" | ||||||
|  | tablet: "Platkomputilo" | ||||||
|  | auto: "Aŭtomate" | ||||||
| _emailUnavailable: | _emailUnavailable: | ||||||
|   used: "La retpoŝto jam estas uzita." |   used: "La retpoŝto jam estas uzita." | ||||||
|   format: "Nevalida formato." |   format: "Nevalida formato." | ||||||
|  | @ -688,6 +697,7 @@ _ffVisibility: | ||||||
|   followers: "Nur al sekvantoj" |   followers: "Nur al sekvantoj" | ||||||
|   private: "Malpublikigita" |   private: "Malpublikigita" | ||||||
| _signup: | _signup: | ||||||
|  |   almostThere: "Preskaŭ plenumita" | ||||||
|   emailAddressInfo: "Entajpu vian retpoŝton" |   emailAddressInfo: "Entajpu vian retpoŝton" | ||||||
| _accountDelete: | _accountDelete: | ||||||
|   accountDelete: "Forigi konton" |   accountDelete: "Forigi konton" | ||||||
|  | @ -698,7 +708,7 @@ _ad: | ||||||
| _forgotPassword: | _forgotPassword: | ||||||
|   enterEmail: "Entajpu la retpoŝton kiun vi registrigis al via konto. Ligilo por restarigi pasvorton estos sendita al la retadreso." |   enterEmail: "Entajpu la retpoŝton kiun vi registrigis al via konto. Ligilo por restarigi pasvorton estos sendita al la retadreso." | ||||||
| _gallery: | _gallery: | ||||||
|   my: "Mia afiŝo" |   my: "Miaj afiŝoj" | ||||||
|   liked: "Ŝatitaj notoj" |   liked: "Ŝatitaj notoj" | ||||||
|   like: "Ŝati" |   like: "Ŝati" | ||||||
| _email: | _email: | ||||||
|  | @ -877,14 +887,18 @@ _cw: | ||||||
|   chars: "{count} literoj" |   chars: "{count} literoj" | ||||||
|   files: "{count} dosiero(j)" |   files: "{count} dosiero(j)" | ||||||
| _poll: | _poll: | ||||||
|   choiceN: "Balotilo {n}" |   choiceN: "Ebla voĉdono {n}" | ||||||
|   noMore: "Oni ne povas aldoni pli." |   noMore: "Oni ne povas aldoni pli" | ||||||
|  |   canMultipleVote: "Permesi plurelekton" | ||||||
|   expiration: "Limtempo" |   expiration: "Limtempo" | ||||||
|   infinite: "Neniam" |  | ||||||
|   deadlineTime: "hor" |   deadlineTime: "hor" | ||||||
|   votesCount: "{n} balotiloj" |   duration: "Daŭro" | ||||||
|   vote: "Baloti" |   votesCount: "{n} voĉoj" | ||||||
|   closed: "Oni jam balotis ĝin" |   totalVotes: "Sume {n} voĉoj" | ||||||
|  |   vote: "Voĉdoni" | ||||||
|  |   showResult: "Vidi la rezultojn" | ||||||
|  |   voted: "Voĉdonita" | ||||||
|  |   closed: "Finita" | ||||||
| _visibility: | _visibility: | ||||||
|   public: "Publika" |   public: "Publika" | ||||||
|   publicDescription: "Publikigi al ĉiuj en la Fediverso" |   publicDescription: "Publikigi al ĉiuj en la Fediverso" | ||||||
|  | @ -1089,7 +1103,7 @@ _notification: | ||||||
|   youGotReply: "{name} respondis" |   youGotReply: "{name} respondis" | ||||||
|   youGotQuote: "{name} citis" |   youGotQuote: "{name} citis" | ||||||
|   youRenoted: "{name} plusendis" |   youRenoted: "{name} plusendis" | ||||||
|   youGotPoll: "{name} balotis" |   youGotPoll: "{name} voĉdonis" | ||||||
|   youGotMessagingMessageFromUser: "{name} sendis al vi mesaĝon" |   youGotMessagingMessageFromUser: "{name} sendis al vi mesaĝon" | ||||||
|   youGotMessagingMessageFromGroup: "Oni sendis al la grupo {name} mesaĝon" |   youGotMessagingMessageFromGroup: "Oni sendis al la grupo {name} mesaĝon" | ||||||
|   youWereFollowed: "Eksekvis vin" |   youWereFollowed: "Eksekvis vin" | ||||||
|  | @ -1104,6 +1118,7 @@ _notification: | ||||||
|     renote: "Plusendoj" |     renote: "Plusendoj" | ||||||
|     quote: "Citi" |     quote: "Citi" | ||||||
|     reaction: "Reagoj" |     reaction: "Reagoj" | ||||||
|  |     pollVote: "Voĉdonoj en balotoj" | ||||||
|     receiveFollowRequest: "Ricevi peton de sekvado" |     receiveFollowRequest: "Ricevi peton de sekvado" | ||||||
|     followRequestAccepted: "Akceptita peto de sekvado" |     followRequestAccepted: "Akceptita peto de sekvado" | ||||||
|     groupInvited: "Invitita al grupo" |     groupInvited: "Invitita al grupo" | ||||||
|  |  | ||||||
|  | @ -595,6 +595,8 @@ smtpSecure: "SMTP 接続に暗黙的なSSL/TLSを使用する" | ||||||
| smtpSecureInfo: "STARTTLS使用時はオフにします。" | smtpSecureInfo: "STARTTLS使用時はオフにします。" | ||||||
| testEmail: "配信テスト" | testEmail: "配信テスト" | ||||||
| wordMute: "ワードミュート" | wordMute: "ワードミュート" | ||||||
|  | regexpError: "正規表現エラー" | ||||||
|  | regexpErrorDescription: "{tab}ワードミュートの{line}行目の正規表現にエラーが発生しました:" | ||||||
| instanceMute: "インスタンスミュート" | instanceMute: "インスタンスミュート" | ||||||
| userSaysSomething: "{name}が何かを言いました" | userSaysSomething: "{name}が何かを言いました" | ||||||
| makeActive: "アクティブにする" | makeActive: "アクティブにする" | ||||||
|  | @ -829,6 +831,8 @@ smartphone: "スマートフォン" | ||||||
| tablet: "タブレット" | tablet: "タブレット" | ||||||
| auto: "自動" | auto: "自動" | ||||||
| themeColor: "テーマカラー" | themeColor: "テーマカラー" | ||||||
|  | size: "サイズ" | ||||||
|  | numberOfColumn: "列の数" | ||||||
| 
 | 
 | ||||||
| _emailUnavailable: | _emailUnavailable: | ||||||
|   used: "既に使用されています" |   used: "既に使用されています" | ||||||
|  |  | ||||||
|  | @ -594,6 +594,8 @@ smtpSecure: "Použiť implicitné SSL/TLS pre SMTP spojenia" | ||||||
| smtpSecureInfo: "Toto vypnite keď používate STARTTLS" | smtpSecureInfo: "Toto vypnite keď používate STARTTLS" | ||||||
| testEmail: "Doručenie testovacieho emailu" | testEmail: "Doručenie testovacieho emailu" | ||||||
| wordMute: "Stíšenie slova" | wordMute: "Stíšenie slova" | ||||||
|  | regexpError: "Chyba v regulárnom výraze" | ||||||
|  | regexpErrorDescription: "Na riadku {line} sa vyskytla chyba v stíšenom slove {tab}." | ||||||
| instanceMute: "Stíšené servery" | instanceMute: "Stíšené servery" | ||||||
| userSaysSomething: "{name} niečo povedal/a" | userSaysSomething: "{name} niečo povedal/a" | ||||||
| makeActive: "Aktivovať" | makeActive: "Aktivovať" | ||||||
|  | @ -827,6 +829,7 @@ overridedDeviceKind: "Typ zariadenia" | ||||||
| smartphone: "Smartfón" | smartphone: "Smartfón" | ||||||
| tablet: "Tablet" | tablet: "Tablet" | ||||||
| auto: "Automaticky" | auto: "Automaticky" | ||||||
|  | themeColor: "Farba témy" | ||||||
| _emailUnavailable: | _emailUnavailable: | ||||||
|   used: "Táto emailová adresa sa už používa" |   used: "Táto emailová adresa sa už používa" | ||||||
|   format: "Formát emailovej adresy je nesprávny" |   format: "Formát emailovej adresy je nesprávny" | ||||||
|  |  | ||||||
|  | @ -595,6 +595,8 @@ smtpSecure: "在 SMTP 连接中使用隐式 SSL / TLS" | ||||||
| smtpSecureInfo: "使用STARTTLS时关闭。" | smtpSecureInfo: "使用STARTTLS时关闭。" | ||||||
| testEmail: "邮件发送测试" | testEmail: "邮件发送测试" | ||||||
| wordMute: "文字屏蔽" | wordMute: "文字屏蔽" | ||||||
|  | regexpError: "正则表达式错误" | ||||||
|  | regexpErrorDescription: "{tab} 屏蔽文字的第 {line} 行的正则表达式有错误:" | ||||||
| instanceMute: "实例的屏蔽" | instanceMute: "实例的屏蔽" | ||||||
| userSaysSomething: "{name}说了什么" | userSaysSomething: "{name}说了什么" | ||||||
| makeActive: "启用" | makeActive: "启用" | ||||||
|  | @ -828,6 +830,7 @@ overridedDeviceKind: "设备类型" | ||||||
| smartphone: "智能手机" | smartphone: "智能手机" | ||||||
| tablet: "平板" | tablet: "平板" | ||||||
| auto: "自动" | auto: "自动" | ||||||
|  | themeColor: "主题颜色" | ||||||
| _emailUnavailable: | _emailUnavailable: | ||||||
|   used: "已经被使用过" |   used: "已经被使用过" | ||||||
|   format: "无效的格式" |   format: "无效的格式" | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| { | { | ||||||
| 	"name": "misskey", | 	"name": "misskey", | ||||||
| 	"version": "12.105.0", | 	"version": "12.106.0", | ||||||
| 	"codename": "indigo", | 	"codename": "indigo", | ||||||
| 	"repository": { | 	"repository": { | ||||||
| 		"type": "git", | 		"type": "git", | ||||||
|  |  | ||||||
|  | @ -0,0 +1,64 @@ | ||||||
|  | const RE2 = require('re2'); | ||||||
|  | const { MigrationInterface, QueryRunner } = require("typeorm"); | ||||||
|  | 
 | ||||||
|  | module.exports = class convertHardMutes1644010796173 { | ||||||
|  |     name = 'convertHardMutes1644010796173' | ||||||
|  | 
 | ||||||
|  |     async up(queryRunner) { | ||||||
|  |         let entries = await queryRunner.query(`SELECT "userId", "mutedWords" FROM "user_profile"`); | ||||||
|  |         for(let i = 0; i < entries.length; i++) { | ||||||
|  |             let words = entries[i].mutedWords | ||||||
|  |                 .map(line => { | ||||||
|  |                     const regexp = line.join(" ").match(/^\/(.+)\/(.*)$/); | ||||||
|  |                     if (regexp) { | ||||||
|  |                         // convert regexp's
 | ||||||
|  |                         try { | ||||||
|  |                             new RE2(regexp[1], regexp[2]); | ||||||
|  |                             return `/${regexp[1]}/${regexp[2]}`; | ||||||
|  |                         } catch (err) { | ||||||
|  |                             // invalid regex, ignore it
 | ||||||
|  |                             return []; | ||||||
|  |                         } | ||||||
|  |                     } else { | ||||||
|  |                         // remove empty segments
 | ||||||
|  |                         return line.filter(x => x !== ''); | ||||||
|  |                     } | ||||||
|  |                 }) | ||||||
|  |                 // remove empty lines
 | ||||||
|  |                 .filter(x => !(Array.isArray(x) && x.length === 0)); | ||||||
|  | 
 | ||||||
|  |             await queryRunner.connection.createQueryBuilder() | ||||||
|  |                 .update('user_profile') | ||||||
|  |                 .set({ | ||||||
|  |                     mutedWords: words | ||||||
|  |                 }) | ||||||
|  |                 .where('userId = :id', { id: entries[i].userId }) | ||||||
|  |                 .execute(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     async down(queryRunner) { | ||||||
|  |         let entries = await queryRunner.query(`SELECT "userId", "mutedWords" FROM "user_profile"`); | ||||||
|  |         for(let i = 0; i < entries.length; i++) { | ||||||
|  |             let words = entries[i].mutedWords | ||||||
|  |                 .map(line => { | ||||||
|  |                     if (Array.isArray(line)) { | ||||||
|  |                         return line; | ||||||
|  |                     } else { | ||||||
|  |                     	// do not split regex at spaces again
 | ||||||
|  |                         return [line]; | ||||||
|  |                     } | ||||||
|  |                 }) | ||||||
|  |                 // remove empty lines
 | ||||||
|  |                 .filter(x => !(Array.isArray(x) && x.length === 0)); | ||||||
|  | 
 | ||||||
|  |             await queryRunner.connection.createQueryBuilder() | ||||||
|  |                 .update('user_profile') | ||||||
|  |                 .set({ | ||||||
|  |                     mutedWords: words | ||||||
|  |                 }) | ||||||
|  |                 .where('userId = :id', { id: entries[i].userId }) | ||||||
|  |                 .execute(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										31
									
								
								packages/backend/migration/1644481657998-chart-v15.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								packages/backend/migration/1644481657998-chart-v15.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,31 @@ | ||||||
|  | const { MigrationInterface, QueryRunner } = require("typeorm"); | ||||||
|  | 
 | ||||||
|  | module.exports = class chartV151644481657998 { | ||||||
|  |     name = 'chartV151644481657998' | ||||||
|  | 
 | ||||||
|  |     async up(queryRunner) { | ||||||
|  |         await queryRunner.query(`ALTER TABLE "__chart__federation" DROP COLUMN "___instance_total"`); | ||||||
|  |         await queryRunner.query(`ALTER TABLE "__chart__federation" DROP COLUMN "___instance_inc"`); | ||||||
|  |         await queryRunner.query(`ALTER TABLE "__chart__federation" DROP COLUMN "___instance_dec"`); | ||||||
|  |         await queryRunner.query(`ALTER TABLE "__chart_day__federation" DROP COLUMN "___instance_total"`); | ||||||
|  |         await queryRunner.query(`ALTER TABLE "__chart_day__federation" DROP COLUMN "___instance_inc"`); | ||||||
|  |         await queryRunner.query(`ALTER TABLE "__chart_day__federation" DROP COLUMN "___instance_dec"`); | ||||||
|  |         await queryRunner.query(`ALTER TABLE "__chart__federation" ADD "___sub" smallint NOT NULL DEFAULT '0'`); | ||||||
|  |         await queryRunner.query(`ALTER TABLE "__chart__federation" ADD "___pub" smallint NOT NULL DEFAULT '0'`); | ||||||
|  |         await queryRunner.query(`ALTER TABLE "__chart_day__federation" ADD "___sub" smallint NOT NULL DEFAULT '0'`); | ||||||
|  |         await queryRunner.query(`ALTER TABLE "__chart_day__federation" ADD "___pub" smallint NOT NULL DEFAULT '0'`); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     async down(queryRunner) { | ||||||
|  |         await queryRunner.query(`ALTER TABLE "__chart_day__federation" DROP COLUMN "___pub"`); | ||||||
|  |         await queryRunner.query(`ALTER TABLE "__chart_day__federation" DROP COLUMN "___sub"`); | ||||||
|  |         await queryRunner.query(`ALTER TABLE "__chart__federation" DROP COLUMN "___pub"`); | ||||||
|  |         await queryRunner.query(`ALTER TABLE "__chart__federation" DROP COLUMN "___sub"`); | ||||||
|  |         await queryRunner.query(`ALTER TABLE "__chart_day__federation" ADD "___instance_dec" smallint NOT NULL DEFAULT '0'`); | ||||||
|  |         await queryRunner.query(`ALTER TABLE "__chart_day__federation" ADD "___instance_inc" smallint NOT NULL DEFAULT '0'`); | ||||||
|  |         await queryRunner.query(`ALTER TABLE "__chart_day__federation" ADD "___instance_total" integer NOT NULL DEFAULT '0'`); | ||||||
|  |         await queryRunner.query(`ALTER TABLE "__chart__federation" ADD "___instance_dec" smallint NOT NULL DEFAULT '0'`); | ||||||
|  |         await queryRunner.query(`ALTER TABLE "__chart__federation" ADD "___instance_inc" smallint NOT NULL DEFAULT '0'`); | ||||||
|  |         await queryRunner.query(`ALTER TABLE "__chart__federation" ADD "___instance_total" integer NOT NULL DEFAULT '0'`); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,15 @@ | ||||||
|  | const { MigrationInterface, QueryRunner } = require("typeorm"); | ||||||
|  | 
 | ||||||
|  | module.exports = class followingIndexes1644551208096 { | ||||||
|  |     name = 'followingIndexes1644551208096' | ||||||
|  | 
 | ||||||
|  |     async up(queryRunner) { | ||||||
|  |         await queryRunner.query(`CREATE INDEX "IDX_4ccd2239268ebbd1b35e318754" ON "following" ("followerHost") `); | ||||||
|  |         await queryRunner.query(`CREATE INDEX "IDX_fcdafee716dfe9c3b5fde90f30" ON "following" ("followeeHost") `); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     async down(queryRunner) { | ||||||
|  |         await queryRunner.query(`DROP INDEX "public"."IDX_fcdafee716dfe9c3b5fde90f30"`); | ||||||
|  |         await queryRunner.query(`DROP INDEX "public"."IDX_4ccd2239268ebbd1b35e318754"`); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -123,7 +123,7 @@ | ||||||
| 		"ms": "3.0.0-canary.1", | 		"ms": "3.0.0-canary.1", | ||||||
| 		"multer": "1.4.4", | 		"multer": "1.4.4", | ||||||
| 		"nested-property": "4.0.0", | 		"nested-property": "4.0.0", | ||||||
| 		"node-fetch": "2.6.1", | 		"node-fetch": "2.6.7", | ||||||
| 		"nodemailer": "6.7.2", | 		"nodemailer": "6.7.2", | ||||||
| 		"os-utils": "0.0.14", | 		"os-utils": "0.0.14", | ||||||
| 		"parse5": "6.0.1", | 		"parse5": "6.0.1", | ||||||
|  |  | ||||||
|  | @ -11,26 +11,31 @@ type UserLike = { | ||||||
| 	id: User['id']; | 	id: User['id']; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export async function checkWordMute(note: NoteLike, me: UserLike | null | undefined, mutedWords: string[][]): Promise<boolean> { | export async function checkWordMute(note: NoteLike, me: UserLike | null | undefined, mutedWords: Array<string | string[]>): Promise<boolean> { | ||||||
| 	// 自分自身
 | 	// 自分自身
 | ||||||
| 	if (me && (note.userId === me.id)) return false; | 	if (me && (note.userId === me.id)) return false; | ||||||
| 
 | 
 | ||||||
| 	const words = mutedWords | 	if (mutedWords.length > 0) { | ||||||
| 		// Clean up
 |  | ||||||
| 		.map(xs => xs.filter(x => x !== '')) |  | ||||||
| 		.filter(xs => xs.length > 0); |  | ||||||
| 
 |  | ||||||
| 	if (words.length > 0) { |  | ||||||
| 		if (note.text == null) return false; | 		if (note.text == null) return false; | ||||||
| 
 | 
 | ||||||
| 		const matched = words.some(and => | 		const matched = mutedWords.some(filter => { | ||||||
| 			and.every(keyword => { | 			if (Array.isArray(filter)) { | ||||||
| 				const regexp = keyword.match(/^\/(.+)\/(.*)$/); | 				return filter.every(keyword => note.text!.includes(keyword)); | ||||||
| 				if (regexp) { | 			} else { | ||||||
|  | 				// represents RegExp
 | ||||||
|  | 				const regexp = filter.match(/^\/(.+)\/(.*)$/); | ||||||
|  | 
 | ||||||
|  | 				// This should never happen due to input sanitisation.
 | ||||||
|  | 				if (!regexp) return false; | ||||||
|  | 
 | ||||||
|  | 				try { | ||||||
| 					return new RE2(regexp[1], regexp[2]).test(note.text!); | 					return new RE2(regexp[1], regexp[2]).test(note.text!); | ||||||
|  | 				} catch (err) { | ||||||
|  | 					// This should never happen due to input sanitisation.
 | ||||||
|  | 					return false; | ||||||
| 				} | 				} | ||||||
| 				return note.text!.includes(keyword); | 			} | ||||||
| 			})); | 		}); | ||||||
| 
 | 
 | ||||||
| 		if (matched) return true; | 		if (matched) return true; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -41,6 +41,7 @@ export class Following { | ||||||
| 	public follower: User | null; | 	public follower: User | null; | ||||||
| 
 | 
 | ||||||
| 	//#region Denormalized fields
 | 	//#region Denormalized fields
 | ||||||
|  | 	@Index() | ||||||
| 	@Column('varchar', { | 	@Column('varchar', { | ||||||
| 		length: 128, nullable: true, | 		length: 128, nullable: true, | ||||||
| 		comment: '[Denormalized]', | 		comment: '[Denormalized]', | ||||||
|  | @ -59,6 +60,7 @@ export class Following { | ||||||
| 	}) | 	}) | ||||||
| 	public followerSharedInbox: string | null; | 	public followerSharedInbox: string | null; | ||||||
| 
 | 
 | ||||||
|  | 	@Index() | ||||||
| 	@Column('varchar', { | 	@Column('varchar', { | ||||||
| 		length: 128, nullable: true, | 		length: 128, nullable: true, | ||||||
| 		comment: '[Denormalized]', | 		comment: '[Denormalized]', | ||||||
|  |  | ||||||
|  | @ -258,6 +258,11 @@ export default function() { | ||||||
| 	processDb(dbQueue); | 	processDb(dbQueue); | ||||||
| 	processObjectStorage(objectStorageQueue); | 	processObjectStorage(objectStorageQueue); | ||||||
| 
 | 
 | ||||||
|  | 	systemQueue.add('tickCharts', { | ||||||
|  | 	}, { | ||||||
|  | 		repeat: { cron: '55 * * * *' }, | ||||||
|  | 	}); | ||||||
|  | 
 | ||||||
| 	systemQueue.add('resyncCharts', { | 	systemQueue.add('resyncCharts', { | ||||||
| 	}, { | 	}, { | ||||||
| 		repeat: { cron: '0 0 * * *' }, | 		repeat: { cron: '0 0 * * *' }, | ||||||
|  |  | ||||||
|  | @ -1,8 +1,10 @@ | ||||||
| import * as Bull from 'bull'; | import * as Bull from 'bull'; | ||||||
|  | import { tickCharts } from './tick-charts'; | ||||||
| import { resyncCharts } from './resync-charts'; | import { resyncCharts } from './resync-charts'; | ||||||
| import { cleanCharts } from './clean-charts'; | import { cleanCharts } from './clean-charts'; | ||||||
| 
 | 
 | ||||||
| const jobs = { | const jobs = { | ||||||
|  | 	tickCharts, | ||||||
| 	resyncCharts, | 	resyncCharts, | ||||||
| 	cleanCharts, | 	cleanCharts, | ||||||
| } as Record<string, Bull.ProcessCallbackFunction<Record<string, unknown>> | Bull.ProcessPromiseFunction<Record<string, unknown>>>; | } as Record<string, Bull.ProcessCallbackFunction<Record<string, unknown>> | Bull.ProcessPromiseFunction<Record<string, unknown>>>; | ||||||
|  |  | ||||||
							
								
								
									
										28
									
								
								packages/backend/src/queue/processors/system/tick-charts.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								packages/backend/src/queue/processors/system/tick-charts.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,28 @@ | ||||||
|  | import * as Bull from 'bull'; | ||||||
|  | 
 | ||||||
|  | import { queueLogger } from '../../logger'; | ||||||
|  | import { activeUsersChart, driveChart, federationChart, hashtagChart, instanceChart, notesChart, perUserDriveChart, perUserFollowingChart, perUserNotesChart, perUserReactionsChart, usersChart, apRequestChart } from '@/services/chart/index'; | ||||||
|  | 
 | ||||||
|  | const logger = queueLogger.createSubLogger('tick-charts'); | ||||||
|  | 
 | ||||||
|  | export async function tickCharts(job: Bull.Job<Record<string, unknown>>, done: any): Promise<void> { | ||||||
|  | 	logger.info(`Tick charts...`); | ||||||
|  | 
 | ||||||
|  | 	await Promise.all([ | ||||||
|  | 		federationChart.tick(false), | ||||||
|  | 		notesChart.tick(false), | ||||||
|  | 		usersChart.tick(false), | ||||||
|  | 		activeUsersChart.tick(false), | ||||||
|  | 		instanceChart.tick(false), | ||||||
|  | 		perUserNotesChart.tick(false), | ||||||
|  | 		driveChart.tick(false), | ||||||
|  | 		perUserReactionsChart.tick(false), | ||||||
|  | 		hashtagChart.tick(false), | ||||||
|  | 		perUserFollowingChart.tick(false), | ||||||
|  | 		perUserDriveChart.tick(false), | ||||||
|  | 		apRequestChart.tick(false), | ||||||
|  | 	]); | ||||||
|  | 
 | ||||||
|  | 	logger.succ(`All charts successfully ticked.`); | ||||||
|  | 	done(); | ||||||
|  | } | ||||||
|  | @ -1,22 +0,0 @@ | ||||||
| import define from '../../define'; |  | ||||||
| import { driveChart, notesChart, usersChart } from '@/services/chart/index'; |  | ||||||
| import { insertModerationLog } from '@/services/insert-moderation-log'; |  | ||||||
| 
 |  | ||||||
| export const meta = { |  | ||||||
| 	tags: ['admin'], |  | ||||||
| 
 |  | ||||||
| 	requireCredential: true, |  | ||||||
| 	requireModerator: true, |  | ||||||
| } as const; |  | ||||||
| 
 |  | ||||||
| // eslint-disable-next-line import/no-default-export
 |  | ||||||
| export default define(meta, async (ps, me) => { |  | ||||||
| 	insertModerationLog(me, 'chartResync'); |  | ||||||
| 
 |  | ||||||
| 	driveChart.resync(); |  | ||||||
| 	notesChart.resync(); |  | ||||||
| 	usersChart.resync(); |  | ||||||
| 
 |  | ||||||
| 	// TODO: ユーザーごとのチャートもキューに入れて更新する
 |  | ||||||
| 	// TODO: インスタンスごとのチャートもキューに入れて更新する
 |  | ||||||
| }); |  | ||||||
|  | @ -1,3 +1,4 @@ | ||||||
|  | const RE2 = require('re2'); | ||||||
| import $ from 'cafy'; | import $ from 'cafy'; | ||||||
| import * as mfm from 'mfm-js'; | import * as mfm from 'mfm-js'; | ||||||
| import { ID } from '@/misc/cafy-id'; | import { ID } from '@/misc/cafy-id'; | ||||||
|  | @ -117,7 +118,7 @@ export const meta = { | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		mutedWords: { | 		mutedWords: { | ||||||
| 			validator: $.optional.arr($.arr($.str)), | 			validator: $.optional.arr($.either($.arr($.str.min(1)).min(1), $.str)), | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		mutedInstances: { | 		mutedInstances: { | ||||||
|  | @ -163,6 +164,12 @@ export const meta = { | ||||||
| 			code: 'NO_SUCH_PAGE', | 			code: 'NO_SUCH_PAGE', | ||||||
| 			id: '8e01b590-7eb9-431b-a239-860e086c408e', | 			id: '8e01b590-7eb9-431b-a239-860e086c408e', | ||||||
| 		}, | 		}, | ||||||
|  | 
 | ||||||
|  | 		invalidRegexp: { | ||||||
|  | 			message: 'Invalid Regular Expression.', | ||||||
|  | 			code: 'INVALID_REGEXP', | ||||||
|  | 			id: '0d786918-10df-41cd-8f33-8dec7d9a89a5', | ||||||
|  | 		} | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
| 	res: { | 	res: { | ||||||
|  | @ -191,6 +198,18 @@ export default define(meta, async (ps, _user, token) => { | ||||||
| 	if (ps.avatarId !== undefined) updates.avatarId = ps.avatarId; | 	if (ps.avatarId !== undefined) updates.avatarId = ps.avatarId; | ||||||
| 	if (ps.bannerId !== undefined) updates.bannerId = ps.bannerId; | 	if (ps.bannerId !== undefined) updates.bannerId = ps.bannerId; | ||||||
| 	if (ps.mutedWords !== undefined) { | 	if (ps.mutedWords !== undefined) { | ||||||
|  | 		// validate regular expression syntax
 | ||||||
|  | 		ps.mutedWords.filter(x => !Array.isArray(x)).forEach(x => { | ||||||
|  | 			const regexp = x.match(/^\/(.+)\/(.*)$/); | ||||||
|  | 			if (!regexp) throw new ApiError(meta.errors.invalidRegexp); | ||||||
|  | 
 | ||||||
|  | 			try { | ||||||
|  | 				new RE2(regexp[1], regexp[2]); | ||||||
|  | 			} catch (err) { | ||||||
|  | 				throw new ApiError(meta.errors.invalidRegexp); | ||||||
|  | 			} | ||||||
|  | 		}); | ||||||
|  | 
 | ||||||
| 		profileUpdates.mutedWords = ps.mutedWords; | 		profileUpdates.mutedWords = ps.mutedWords; | ||||||
| 		profileUpdates.enableWordMute = ps.mutedWords.length > 0; | 		profileUpdates.enableWordMute = ps.mutedWords.length > 0; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -18,7 +18,12 @@ export default class ActiveUsersChart extends Chart<typeof schema> { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@autobind | 	@autobind | ||||||
| 	protected async queryCurrentState(): Promise<Partial<KVs<typeof schema>>> { | 	protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> { | ||||||
|  | 		return {}; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@autobind | ||||||
|  | 	protected async tickMinor(): Promise<Partial<KVs<typeof schema>>> { | ||||||
| 		return {}; | 		return {}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -12,7 +12,12 @@ export default class ApRequestChart extends Chart<typeof schema> { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@autobind | 	@autobind | ||||||
| 	protected async queryCurrentState(): Promise<Partial<KVs<typeof schema>>> { | 	protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> { | ||||||
|  | 		return {}; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@autobind | ||||||
|  | 	protected async tickMinor(): Promise<Partial<KVs<typeof schema>>> { | ||||||
| 		return {}; | 		return {}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -15,7 +15,12 @@ export default class DriveChart extends Chart<typeof schema> { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@autobind | 	@autobind | ||||||
| 	protected async queryCurrentState(): Promise<Partial<KVs<typeof schema>>> { | 	protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> { | ||||||
|  | 		return {}; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@autobind | ||||||
|  | 	protected async tickMinor(): Promise<Partial<KVs<typeof schema>>> { | ||||||
| 		return {}; | 		return {}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -3,12 +3,11 @@ import Chart from '../../core'; | ||||||
| export const name = 'federation'; | export const name = 'federation'; | ||||||
| 
 | 
 | ||||||
| export const schema = { | export const schema = { | ||||||
| 	'instance.total': { accumulate: true }, |  | ||||||
| 	'instance.inc': { range: 'small' }, |  | ||||||
| 	'instance.dec': { range: 'small' }, |  | ||||||
| 	'deliveredInstances': { uniqueIncrement: true, range: 'small' }, | 	'deliveredInstances': { uniqueIncrement: true, range: 'small' }, | ||||||
| 	'inboxInstances': { uniqueIncrement: true, range: 'small' }, | 	'inboxInstances': { uniqueIncrement: true, range: 'small' }, | ||||||
| 	'stalled': { uniqueIncrement: true, range: 'small' }, | 	'stalled': { uniqueIncrement: true, range: 'small' }, | ||||||
|  | 	'sub': { accumulate: true, range: 'small' }, | ||||||
|  | 	'pub': { accumulate: true, range: 'small' }, | ||||||
| } as const; | } as const; | ||||||
| 
 | 
 | ||||||
| export const entity = Chart.schemaToEntity(name, schema); | export const entity = Chart.schemaToEntity(name, schema); | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| import autobind from 'autobind-decorator'; | import autobind from 'autobind-decorator'; | ||||||
| import Chart, { KVs } from '../core'; | import Chart, { KVs } from '../core'; | ||||||
| import { Instances } from '@/models/index'; | import { Followings } from '@/models/index'; | ||||||
| import { name, schema } from './entities/federation'; | import { name, schema } from './entities/federation'; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  | @ -13,23 +13,30 @@ export default class FederationChart extends Chart<typeof schema> { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@autobind | 	@autobind | ||||||
| 	protected async queryCurrentState(): Promise<Partial<KVs<typeof schema>>> { | 	protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> { | ||||||
| 		const [total] = await Promise.all([ |  | ||||||
| 			Instances.count({}), |  | ||||||
| 		]); |  | ||||||
| 
 |  | ||||||
| 		return { | 		return { | ||||||
| 			'instance.total': total, |  | ||||||
| 		}; | 		}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@autobind | 	@autobind | ||||||
| 	public async update(isAdditional: boolean): Promise<void> { | 	protected async tickMinor(): Promise<Partial<KVs<typeof schema>>> { | ||||||
| 		await this.commit({ | 		const [sub, pub] = await Promise.all([ | ||||||
| 			'instance.total': isAdditional ? 1 : -1, | 			Followings.createQueryBuilder('following') | ||||||
| 			'instance.inc': isAdditional ? 1 : 0, | 				.select('COUNT(DISTINCT following.followeeHost)') | ||||||
| 			'instance.dec': isAdditional ? 0 : 1, | 				.where('following.followeeHost IS NOT NULL') | ||||||
| 		}); | 				.getRawOne() | ||||||
|  | 				.then(x => parseInt(x.count, 10)), | ||||||
|  | 			Followings.createQueryBuilder('following') | ||||||
|  | 				.select('COUNT(DISTINCT following.followerHost)') | ||||||
|  | 				.where('following.followerHost IS NOT NULL') | ||||||
|  | 				.getRawOne() | ||||||
|  | 				.then(x => parseInt(x.count, 10)), | ||||||
|  | 		]); | ||||||
|  | 
 | ||||||
|  | 		return { | ||||||
|  | 			'sub': sub, | ||||||
|  | 			'pub': pub, | ||||||
|  | 		}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@autobind | 	@autobind | ||||||
|  |  | ||||||
|  | @ -14,7 +14,12 @@ export default class HashtagChart extends Chart<typeof schema> { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@autobind | 	@autobind | ||||||
| 	protected async queryCurrentState(): Promise<Partial<KVs<typeof schema>>> { | 	protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> { | ||||||
|  | 		return {}; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@autobind | ||||||
|  | 	protected async tickMinor(): Promise<Partial<KVs<typeof schema>>> { | ||||||
| 		return {}; | 		return {}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -16,7 +16,7 @@ export default class InstanceChart extends Chart<typeof schema> { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@autobind | 	@autobind | ||||||
| 	protected async queryCurrentState(group: string): Promise<Partial<KVs<typeof schema>>> { | 	protected async tickMajor(group: string): Promise<Partial<KVs<typeof schema>>> { | ||||||
| 		const [ | 		const [ | ||||||
| 			notesCount, | 			notesCount, | ||||||
| 			usersCount, | 			usersCount, | ||||||
|  | @ -42,6 +42,11 @@ export default class InstanceChart extends Chart<typeof schema> { | ||||||
| 		}; | 		}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@autobind | ||||||
|  | 	protected async tickMinor(): Promise<Partial<KVs<typeof schema>>> { | ||||||
|  | 		return {}; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	@autobind | 	@autobind | ||||||
| 	public async requestReceived(host: string): Promise<void> { | 	public async requestReceived(host: string): Promise<void> { | ||||||
| 		await this.commit({ | 		await this.commit({ | ||||||
|  |  | ||||||
|  | @ -15,7 +15,7 @@ export default class NotesChart extends Chart<typeof schema> { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@autobind | 	@autobind | ||||||
| 	protected async queryCurrentState(): Promise<Partial<KVs<typeof schema>>> { | 	protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> { | ||||||
| 		const [localCount, remoteCount] = await Promise.all([ | 		const [localCount, remoteCount] = await Promise.all([ | ||||||
| 			Notes.count({ userHost: null }), | 			Notes.count({ userHost: null }), | ||||||
| 			Notes.count({ userHost: Not(IsNull()) }), | 			Notes.count({ userHost: Not(IsNull()) }), | ||||||
|  | @ -27,6 +27,11 @@ export default class NotesChart extends Chart<typeof schema> { | ||||||
| 		}; | 		}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@autobind | ||||||
|  | 	protected async tickMinor(): Promise<Partial<KVs<typeof schema>>> { | ||||||
|  | 		return {}; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	@autobind | 	@autobind | ||||||
| 	public async update(note: Note, isAdditional: boolean): Promise<void> { | 	public async update(note: Note, isAdditional: boolean): Promise<void> { | ||||||
| 		const prefix = note.userHost === null ? 'local' : 'remote'; | 		const prefix = note.userHost === null ? 'local' : 'remote'; | ||||||
|  |  | ||||||
|  | @ -14,7 +14,7 @@ export default class PerUserDriveChart extends Chart<typeof schema> { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@autobind | 	@autobind | ||||||
| 	protected async queryCurrentState(group: string): Promise<Partial<KVs<typeof schema>>> { | 	protected async tickMajor(group: string): Promise<Partial<KVs<typeof schema>>> { | ||||||
| 		const [count, size] = await Promise.all([ | 		const [count, size] = await Promise.all([ | ||||||
| 			DriveFiles.count({ userId: group }), | 			DriveFiles.count({ userId: group }), | ||||||
| 			DriveFiles.calcDriveUsageOf(group), | 			DriveFiles.calcDriveUsageOf(group), | ||||||
|  | @ -26,6 +26,11 @@ export default class PerUserDriveChart extends Chart<typeof schema> { | ||||||
| 		}; | 		}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@autobind | ||||||
|  | 	protected async tickMinor(): Promise<Partial<KVs<typeof schema>>> { | ||||||
|  | 		return {}; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	@autobind | 	@autobind | ||||||
| 	public async update(file: DriveFile, isAdditional: boolean): Promise<void> { | 	public async update(file: DriveFile, isAdditional: boolean): Promise<void> { | ||||||
| 		const fileSizeKb = file.size / 1000; | 		const fileSizeKb = file.size / 1000; | ||||||
|  |  | ||||||
|  | @ -15,7 +15,7 @@ export default class PerUserFollowingChart extends Chart<typeof schema> { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@autobind | 	@autobind | ||||||
| 	protected async queryCurrentState(group: string): Promise<Partial<KVs<typeof schema>>> { | 	protected async tickMajor(group: string): Promise<Partial<KVs<typeof schema>>> { | ||||||
| 		const [ | 		const [ | ||||||
| 			localFollowingsCount, | 			localFollowingsCount, | ||||||
| 			localFollowersCount, | 			localFollowersCount, | ||||||
|  | @ -36,6 +36,11 @@ export default class PerUserFollowingChart extends Chart<typeof schema> { | ||||||
| 		}; | 		}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@autobind | ||||||
|  | 	protected async tickMinor(): Promise<Partial<KVs<typeof schema>>> { | ||||||
|  | 		return {}; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	@autobind | 	@autobind | ||||||
| 	public async update(follower: { id: User['id']; host: User['host']; }, followee: { id: User['id']; host: User['host']; }, isFollow: boolean): Promise<void> { | 	public async update(follower: { id: User['id']; host: User['host']; }, followee: { id: User['id']; host: User['host']; }, isFollow: boolean): Promise<void> { | ||||||
| 		const prefixFollower = Users.isLocalUser(follower) ? 'local' : 'remote'; | 		const prefixFollower = Users.isLocalUser(follower) ? 'local' : 'remote'; | ||||||
|  |  | ||||||
|  | @ -15,7 +15,7 @@ export default class PerUserNotesChart extends Chart<typeof schema> { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@autobind | 	@autobind | ||||||
| 	protected async queryCurrentState(group: string): Promise<Partial<KVs<typeof schema>>> { | 	protected async tickMajor(group: string): Promise<Partial<KVs<typeof schema>>> { | ||||||
| 		const [count] = await Promise.all([ | 		const [count] = await Promise.all([ | ||||||
| 			Notes.count({ userId: group }), | 			Notes.count({ userId: group }), | ||||||
| 		]); | 		]); | ||||||
|  | @ -25,6 +25,11 @@ export default class PerUserNotesChart extends Chart<typeof schema> { | ||||||
| 		}; | 		}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@autobind | ||||||
|  | 	protected async tickMinor(): Promise<Partial<KVs<typeof schema>>> { | ||||||
|  | 		return {}; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	@autobind | 	@autobind | ||||||
| 	public async update(user: { id: User['id'] }, note: Note, isAdditional: boolean): Promise<void> { | 	public async update(user: { id: User['id'] }, note: Note, isAdditional: boolean): Promise<void> { | ||||||
| 		await this.commit({ | 		await this.commit({ | ||||||
|  |  | ||||||
|  | @ -15,7 +15,12 @@ export default class PerUserReactionsChart extends Chart<typeof schema> { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@autobind | 	@autobind | ||||||
| 	protected async queryCurrentState(group: string): Promise<Partial<KVs<typeof schema>>> { | 	protected async tickMajor(group: string): Promise<Partial<KVs<typeof schema>>> { | ||||||
|  | 		return {}; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@autobind | ||||||
|  | 	protected async tickMinor(): Promise<Partial<KVs<typeof schema>>> { | ||||||
| 		return {}; | 		return {}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -14,12 +14,17 @@ export default class TestGroupedChart extends Chart<typeof schema> { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@autobind | 	@autobind | ||||||
| 	protected async queryCurrentState(group: string): Promise<Partial<KVs<typeof schema>>> { | 	protected async tickMajor(group: string): Promise<Partial<KVs<typeof schema>>> { | ||||||
| 		return { | 		return { | ||||||
| 			'foo.total': this.total[group], | 			'foo.total': this.total[group], | ||||||
| 		}; | 		}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@autobind | ||||||
|  | 	protected async tickMinor(): Promise<Partial<KVs<typeof schema>>> { | ||||||
|  | 		return {}; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	@autobind | 	@autobind | ||||||
| 	public async increment(group: string): Promise<void> { | 	public async increment(group: string): Promise<void> { | ||||||
| 		if (this.total[group] == null) this.total[group] = 0; | 		if (this.total[group] == null) this.total[group] = 0; | ||||||
|  |  | ||||||
|  | @ -12,7 +12,12 @@ export default class TestIntersectionChart extends Chart<typeof schema> { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@autobind | 	@autobind | ||||||
| 	protected async queryCurrentState(): Promise<Partial<KVs<typeof schema>>> { | 	protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> { | ||||||
|  | 		return {}; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@autobind | ||||||
|  | 	protected async tickMinor(): Promise<Partial<KVs<typeof schema>>> { | ||||||
| 		return {}; | 		return {}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -12,7 +12,12 @@ export default class TestUniqueChart extends Chart<typeof schema> { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@autobind | 	@autobind | ||||||
| 	protected async queryCurrentState(): Promise<Partial<KVs<typeof schema>>> { | 	protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> { | ||||||
|  | 		return {}; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@autobind | ||||||
|  | 	protected async tickMinor(): Promise<Partial<KVs<typeof schema>>> { | ||||||
| 		return {}; | 		return {}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -14,12 +14,17 @@ export default class TestChart extends Chart<typeof schema> { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@autobind | 	@autobind | ||||||
| 	protected async queryCurrentState(): Promise<Partial<KVs<typeof schema>>> { | 	protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> { | ||||||
| 		return { | 		return { | ||||||
| 			'foo.total': this.total, | 			'foo.total': this.total, | ||||||
| 		}; | 		}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@autobind | ||||||
|  | 	protected async tickMinor(): Promise<Partial<KVs<typeof schema>>> { | ||||||
|  | 		return {}; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	@autobind | 	@autobind | ||||||
| 	public async increment(): Promise<void> { | 	public async increment(): Promise<void> { | ||||||
| 		this.total++; | 		this.total++; | ||||||
|  |  | ||||||
|  | @ -15,7 +15,7 @@ export default class UsersChart extends Chart<typeof schema> { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@autobind | 	@autobind | ||||||
| 	protected async queryCurrentState(): Promise<Partial<KVs<typeof schema>>> { | 	protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> { | ||||||
| 		const [localCount, remoteCount] = await Promise.all([ | 		const [localCount, remoteCount] = await Promise.all([ | ||||||
| 			Users.count({ host: null }), | 			Users.count({ host: null }), | ||||||
| 			Users.count({ host: Not(IsNull()) }), | 			Users.count({ host: Not(IsNull()) }), | ||||||
|  | @ -27,6 +27,11 @@ export default class UsersChart extends Chart<typeof schema> { | ||||||
| 		}; | 		}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@autobind | ||||||
|  | 	protected async tickMinor(): Promise<Partial<KVs<typeof schema>>> { | ||||||
|  | 		return {}; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	@autobind | 	@autobind | ||||||
| 	public async update(user: { id: User['id'], host: User['host'] }, isAdditional: boolean): Promise<void> { | 	public async update(user: { id: User['id'], host: User['host'] }, isAdditional: boolean): Promise<void> { | ||||||
| 		const prefix = Users.isLocalUser(user) ? 'local' : 'remote'; | 		const prefix = Users.isLocalUser(user) ? 'local' : 'remote'; | ||||||
|  |  | ||||||
|  | @ -81,7 +81,15 @@ export default abstract class Chart<T extends Schema> { | ||||||
| 	protected repositoryForHour: Repository<RawRecord<T>>; | 	protected repositoryForHour: Repository<RawRecord<T>>; | ||||||
| 	protected repositoryForDay: Repository<RawRecord<T>>; | 	protected repositoryForDay: Repository<RawRecord<T>>; | ||||||
| 
 | 
 | ||||||
| 	protected abstract queryCurrentState(group: string | null): Promise<Partial<KVs<T>>>; | 	/** | ||||||
|  | 	 * 1日に一回程度実行されれば良いような計算処理を入れる(主にCASCADE削除などアプリケーション側で感知できない変動によるズレの修正用) | ||||||
|  | 	 */ | ||||||
|  | 	protected abstract tickMajor(group: string | null): Promise<Partial<KVs<T>>>; | ||||||
|  | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * 少なくとも最小スパン内に1回は実行されて欲しい計算処理を入れる | ||||||
|  | 	 */ | ||||||
|  | 	protected abstract tickMinor(group: string | null): Promise<Partial<KVs<T>>>; | ||||||
| 
 | 
 | ||||||
| 	@autobind | 	@autobind | ||||||
| 	private static convertSchemaToColumnDefinitions(schema: Schema): Record<string, { type: string; array?: boolean; default?: any; }> { | 	private static convertSchemaToColumnDefinitions(schema: Schema): Record<string, { type: string; array?: boolean; default?: any; }> { | ||||||
|  | @ -445,8 +453,8 @@ export default abstract class Chart<T extends Schema> { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@autobind | 	@autobind | ||||||
| 	public async resync(group: string | null = null): Promise<void> { | 	public async tick(major: boolean, group: string | null = null): Promise<void> { | ||||||
| 		const data = await this.queryCurrentState(group); | 		const data = major ? await this.tickMajor(group) : await this.tickMinor(group); | ||||||
| 
 | 
 | ||||||
| 		const columns = {} as Record<string, number>; | 		const columns = {} as Record<string, number>; | ||||||
| 		for (const [k, v] of Object.entries(data)) { | 		for (const [k, v] of Object.entries(data)) { | ||||||
|  | @ -480,6 +488,11 @@ export default abstract class Chart<T extends Schema> { | ||||||
| 			update(logHour, logDay)); | 			update(logHour, logDay)); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@autobind | ||||||
|  | 	public resync(group: string | null = null): Promise<void> { | ||||||
|  | 		return this.tick(true, group); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	@autobind | 	@autobind | ||||||
| 	public async clean(): Promise<void> { | 	public async clean(): Promise<void> { | ||||||
| 		const current = dateUTC(Chart.getCurrentDate()); | 		const current = dateUTC(Chart.getCurrentDate()); | ||||||
|  |  | ||||||
|  | @ -1,6 +1,5 @@ | ||||||
| import { Instance } from '@/models/entities/instance'; | import { Instance } from '@/models/entities/instance'; | ||||||
| import { Instances } from '@/models/index'; | import { Instances } from '@/models/index'; | ||||||
| import { federationChart } from '@/services/chart/index'; |  | ||||||
| import { genId } from '@/misc/gen-id'; | import { genId } from '@/misc/gen-id'; | ||||||
| import { toPuny } from '@/misc/convert-host'; | import { toPuny } from '@/misc/convert-host'; | ||||||
| import { Cache } from '@/misc/cache'; | import { Cache } from '@/misc/cache'; | ||||||
|  | @ -23,8 +22,6 @@ export async function registerOrFetchInstanceDoc(host: string): Promise<Instance | ||||||
| 			lastCommunicatedAt: new Date(), | 			lastCommunicatedAt: new Date(), | ||||||
| 		}).then(x => Instances.findOneOrFail(x.identifiers[0])); | 		}).then(x => Instances.findOneOrFail(x.identifiers[0])); | ||||||
| 
 | 
 | ||||||
| 		federationChart.update(true); |  | ||||||
| 
 |  | ||||||
| 		cache.set(host, i); | 		cache.set(host, i); | ||||||
| 		return i; | 		return i; | ||||||
| 	} else { | 	} else { | ||||||
|  |  | ||||||
|  | @ -3134,9 +3134,9 @@ github-from-package@0.0.0: | ||||||
|   integrity sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4= |   integrity sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4= | ||||||
| 
 | 
 | ||||||
| glob-parent@^5.1.0, glob-parent@~5.1.0: | glob-parent@^5.1.0, glob-parent@~5.1.0: | ||||||
|   version "5.1.1" |   version "5.1.2" | ||||||
|   resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" |   resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" | ||||||
|   integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== |   integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== | ||||||
|   dependencies: |   dependencies: | ||||||
|     is-glob "^4.0.1" |     is-glob "^4.0.1" | ||||||
| 
 | 
 | ||||||
|  | @ -3741,14 +3741,7 @@ is-generator-function@^1.0.7: | ||||||
|   resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.7.tgz#d2132e529bb0000a7f80794d4bdf5cd5e5813522" |   resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.7.tgz#d2132e529bb0000a7f80794d4bdf5cd5e5813522" | ||||||
|   integrity sha512-YZc5EwyO4f2kWCax7oegfuSr9mFz1ZvieNYBEjmukLxgXfBUbxAWGVF7GZf0zidYtoBl3WvC07YK0wT76a+Rtw== |   integrity sha512-YZc5EwyO4f2kWCax7oegfuSr9mFz1ZvieNYBEjmukLxgXfBUbxAWGVF7GZf0zidYtoBl3WvC07YK0wT76a+Rtw== | ||||||
| 
 | 
 | ||||||
| is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: | is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: | ||||||
|   version "4.0.1" |  | ||||||
|   resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" |  | ||||||
|   integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== |  | ||||||
|   dependencies: |  | ||||||
|     is-extglob "^2.1.1" |  | ||||||
| 
 |  | ||||||
| is-glob@^4.0.3: |  | ||||||
|   version "4.0.3" |   version "4.0.3" | ||||||
|   resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" |   resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" | ||||||
|   integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== |   integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== | ||||||
|  | @ -4643,17 +4636,10 @@ minipass-sized@^1.0.3: | ||||||
|   dependencies: |   dependencies: | ||||||
|     minipass "^3.0.0" |     minipass "^3.0.0" | ||||||
| 
 | 
 | ||||||
| minipass@^3.0.0: | minipass@^3.0.0, minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3: | ||||||
|   version "3.1.1" |   version "3.1.6" | ||||||
|   resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.1.tgz#7607ce778472a185ad6d89082aa2070f79cedcd5" |   resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.6.tgz#3b8150aa688a711a1521af5e8779c1d3bb4f45ee" | ||||||
|   integrity sha512-UFqVihv6PQgwj8/yTGvl9kPz7xIAY+R5z6XYjRInD3Gk3qx6QGSD6zEcpeG4Dy/lQnv1J6zv8ejV90hyYIKf3w== |   integrity sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ== | ||||||
|   dependencies: |  | ||||||
|     yallist "^4.0.0" |  | ||||||
| 
 |  | ||||||
| minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3: |  | ||||||
|   version "3.1.3" |  | ||||||
|   resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.3.tgz#7d42ff1f39635482e15f9cdb53184deebd5815fd" |  | ||||||
|   integrity sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg== |  | ||||||
|   dependencies: |   dependencies: | ||||||
|     yallist "^4.0.0" |     yallist "^4.0.0" | ||||||
| 
 | 
 | ||||||
|  | @ -4882,10 +4868,12 @@ node-fetch@*: | ||||||
|     fetch-blob "^3.1.4" |     fetch-blob "^3.1.4" | ||||||
|     formdata-polyfill "^4.0.10" |     formdata-polyfill "^4.0.10" | ||||||
| 
 | 
 | ||||||
| node-fetch@2.6.1, node-fetch@^2.6.1: | node-fetch@2.6.7, node-fetch@^2.6.1: | ||||||
|   version "2.6.1" |   version "2.6.7" | ||||||
|   resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" |   resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" | ||||||
|   integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== |   integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== | ||||||
|  |   dependencies: | ||||||
|  |     whatwg-url "^5.0.0" | ||||||
| 
 | 
 | ||||||
| node-fetch@3.0.0-beta.9: | node-fetch@3.0.0-beta.9: | ||||||
|   version "3.0.0-beta.9" |   version "3.0.0-beta.9" | ||||||
|  | @ -4959,9 +4947,9 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: | ||||||
|   integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== |   integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== | ||||||
| 
 | 
 | ||||||
| normalize-url@^4.1.0: | normalize-url@^4.1.0: | ||||||
|   version "4.5.0" |   version "4.5.1" | ||||||
|   resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129" |   resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a" | ||||||
|   integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ== |   integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA== | ||||||
| 
 | 
 | ||||||
| npm-run-path@^5.0.1: | npm-run-path@^5.0.1: | ||||||
|   version "5.0.1" |   version "5.0.1" | ||||||
|  | @ -5293,9 +5281,9 @@ path-key@^4.0.0: | ||||||
|   integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== |   integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== | ||||||
| 
 | 
 | ||||||
| path-parse@^1.0.6: | path-parse@^1.0.6: | ||||||
|   version "1.0.6" |   version "1.0.7" | ||||||
|   resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" |   resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" | ||||||
|   integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== |   integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== | ||||||
| 
 | 
 | ||||||
| path-to-regexp@^6.1.0: | path-to-regexp@^6.1.0: | ||||||
|   version "6.1.0" |   version "6.1.0" | ||||||
|  | @ -6169,20 +6157,11 @@ signal-exit@^3.0.5: | ||||||
|   integrity sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ== |   integrity sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ== | ||||||
| 
 | 
 | ||||||
| simple-concat@^1.0.0: | simple-concat@^1.0.0: | ||||||
|   version "1.0.0" |   version "1.0.1" | ||||||
|   resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.0.tgz#7344cbb8b6e26fb27d66b2fc86f9f6d5997521c6" |   resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" | ||||||
|   integrity sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY= |   integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== | ||||||
| 
 | 
 | ||||||
| simple-get@^4.0.0: | simple-get@^4.0.0, simple-get@^4.0.1: | ||||||
|   version "4.0.0" |  | ||||||
|   resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-4.0.0.tgz#73fa628278d21de83dadd5512d2cc1f4872bd675" |  | ||||||
|   integrity sha512-ZalZGexYr3TA0SwySsr5HlgOOinS4Jsa8YB2GJ6lUNAazyAu4KG/VmzMTwAt2YVXzzVj8QmefmAonZIK2BSGcQ== |  | ||||||
|   dependencies: |  | ||||||
|     decompress-response "^6.0.0" |  | ||||||
|     once "^1.3.1" |  | ||||||
|     simple-concat "^1.0.0" |  | ||||||
| 
 |  | ||||||
| simple-get@^4.0.1: |  | ||||||
|   version "4.0.1" |   version "4.0.1" | ||||||
|   resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-4.0.1.tgz#4a39db549287c979d352112fa03fd99fd6bc3543" |   resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-4.0.1.tgz#4a39db549287c979d352112fa03fd99fd6bc3543" | ||||||
|   integrity sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA== |   integrity sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA== | ||||||
|  | @ -6542,19 +6521,7 @@ tar-stream@^2.1.4, tar-stream@^2.2.0: | ||||||
|     inherits "^2.0.3" |     inherits "^2.0.3" | ||||||
|     readable-stream "^3.1.1" |     readable-stream "^3.1.1" | ||||||
| 
 | 
 | ||||||
| tar@^6.0.2: | tar@^6.0.2, tar@^6.1.2: | ||||||
|   version "6.0.5" |  | ||||||
|   resolved "https://registry.yarnpkg.com/tar/-/tar-6.0.5.tgz#bde815086e10b39f1dcd298e89d596e1535e200f" |  | ||||||
|   integrity sha512-0b4HOimQHj9nXNEAA7zWwMM91Zhhba3pspja6sQbgTpynOJf+bkjBnfybNYzbpLbnwXnbyB4LOREvlyXLkCHSg== |  | ||||||
|   dependencies: |  | ||||||
|     chownr "^2.0.0" |  | ||||||
|     fs-minipass "^2.0.0" |  | ||||||
|     minipass "^3.0.0" |  | ||||||
|     minizlib "^2.1.1" |  | ||||||
|     mkdirp "^1.0.3" |  | ||||||
|     yallist "^4.0.0" |  | ||||||
| 
 |  | ||||||
| tar@^6.1.2: |  | ||||||
|   version "6.1.11" |   version "6.1.11" | ||||||
|   resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621" |   resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621" | ||||||
|   integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA== |   integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA== | ||||||
|  | @ -6653,6 +6620,11 @@ tr46@^3.0.0: | ||||||
|   dependencies: |   dependencies: | ||||||
|     punycode "^2.1.1" |     punycode "^2.1.1" | ||||||
| 
 | 
 | ||||||
|  | tr46@~0.0.3: | ||||||
|  |   version "0.0.3" | ||||||
|  |   resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" | ||||||
|  |   integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= | ||||||
|  | 
 | ||||||
| trace-redirect@1.0.6: | trace-redirect@1.0.6: | ||||||
|   version "1.0.6" |   version "1.0.6" | ||||||
|   resolved "https://registry.yarnpkg.com/trace-redirect/-/trace-redirect-1.0.6.tgz#ac629b5bf8247d30dde5a35fe9811b811075b504" |   resolved "https://registry.yarnpkg.com/trace-redirect/-/trace-redirect-1.0.6.tgz#ac629b5bf8247d30dde5a35fe9811b811075b504" | ||||||
|  | @ -6998,6 +6970,11 @@ web-streams-polyfill@^3.0.3: | ||||||
|   resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.2.0.tgz#a6b74026b38e4885869fb5c589e90b95ccfc7965" |   resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.2.0.tgz#a6b74026b38e4885869fb5c589e90b95ccfc7965" | ||||||
|   integrity sha512-EqPmREeOzttaLRm5HS7io98goBgZ7IVz79aDvqjD0kYXLtFZTc0T/U6wHTPKyIjb+MdN7DFIIX6hgdBEpWmfPA== |   integrity sha512-EqPmREeOzttaLRm5HS7io98goBgZ7IVz79aDvqjD0kYXLtFZTc0T/U6wHTPKyIjb+MdN7DFIIX6hgdBEpWmfPA== | ||||||
| 
 | 
 | ||||||
|  | webidl-conversions@^3.0.0: | ||||||
|  |   version "3.0.1" | ||||||
|  |   resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" | ||||||
|  |   integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= | ||||||
|  | 
 | ||||||
| webidl-conversions@^7.0.0: | webidl-conversions@^7.0.0: | ||||||
|   version "7.0.0" |   version "7.0.0" | ||||||
|   resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" |   resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" | ||||||
|  | @ -7035,6 +7012,14 @@ whatwg-url@^10.0.0: | ||||||
|     tr46 "^3.0.0" |     tr46 "^3.0.0" | ||||||
|     webidl-conversions "^7.0.0" |     webidl-conversions "^7.0.0" | ||||||
| 
 | 
 | ||||||
|  | whatwg-url@^5.0.0: | ||||||
|  |   version "5.0.0" | ||||||
|  |   resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" | ||||||
|  |   integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= | ||||||
|  |   dependencies: | ||||||
|  |     tr46 "~0.0.3" | ||||||
|  |     webidl-conversions "^3.0.0" | ||||||
|  | 
 | ||||||
| which-boxed-primitive@^1.0.2: | which-boxed-primitive@^1.0.2: | ||||||
|   version "1.0.2" |   version "1.0.2" | ||||||
|   resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" |   resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" | ||||||
|  |  | ||||||
|  | @ -213,17 +213,18 @@ export default defineComponent({ | ||||||
| 						data: x.data.slice().reverse(), | 						data: x.data.slice().reverse(), | ||||||
| 						tension: 0.3, | 						tension: 0.3, | ||||||
| 						pointRadius: 0, | 						pointRadius: 0, | ||||||
| 						borderWidth: 2, | 						borderWidth: props.bar ? 0 : 2, | ||||||
| 						borderColor: x.color ? x.color : getColor(i), | 						borderColor: x.color ? x.color : getColor(i), | ||||||
| 						borderDash: x.borderDash || [], | 						borderDash: x.borderDash || [], | ||||||
| 						borderJoinStyle: 'round', | 						borderJoinStyle: 'round', | ||||||
| 						backgroundColor: alpha(x.color ? x.color : getColor(i), 0.1), | 						borderRadius: props.bar ? 3 : undefined, | ||||||
| 						gradient: { | 						backgroundColor: props.bar ? (x.color ? x.color : getColor(i)) : alpha(x.color ? x.color : getColor(i), 0.1), | ||||||
|  | 						gradient: props.bar ? undefined : { | ||||||
| 							backgroundColor: { | 							backgroundColor: { | ||||||
| 								axis: 'y', | 								axis: 'y', | ||||||
| 								colors: { | 								colors: { | ||||||
| 									0: alpha(x.color ? x.color : getColor(i), 0), | 									0: alpha(x.color ? x.color : getColor(i), 0), | ||||||
| 									[maxes[i]]: alpha(x.color ? x.color : getColor(i), 0.1), | 									[maxes[i]]: alpha(x.color ? x.color : getColor(i), 0.15), | ||||||
| 								}, | 								}, | ||||||
| 							}, | 							}, | ||||||
| 						}, | 						}, | ||||||
|  | @ -248,6 +249,7 @@ export default defineComponent({ | ||||||
| 						x: { | 						x: { | ||||||
| 							type: 'time', | 							type: 'time', | ||||||
| 							stacked: props.stacked, | 							stacked: props.stacked, | ||||||
|  | 							offset: false, | ||||||
| 							time: { | 							time: { | ||||||
| 								stepSize: 1, | 								stepSize: 1, | ||||||
| 								unit: props.span === 'day' ? 'month' : 'day', | 								unit: props.span === 'day' ? 'month' : 'day', | ||||||
|  | @ -271,6 +273,7 @@ export default defineComponent({ | ||||||
| 						y: { | 						y: { | ||||||
| 							position: 'left', | 							position: 'left', | ||||||
| 							stacked: props.stacked, | 							stacked: props.stacked, | ||||||
|  | 							suggestedMax: 100, | ||||||
| 							grid: { | 							grid: { | ||||||
| 								color: gridColor, | 								color: gridColor, | ||||||
| 								borderColor: 'rgb(0, 0, 0, 0)', | 								borderColor: 'rgb(0, 0, 0, 0)', | ||||||
|  | @ -308,7 +311,7 @@ export default defineComponent({ | ||||||
| 							}, | 							}, | ||||||
| 							external: externalTooltipHandler, | 							external: externalTooltipHandler, | ||||||
| 						}, | 						}, | ||||||
| 						zoom: { | 						zoom: props.detailed ? { | ||||||
| 							pan: { | 							pan: { | ||||||
| 								enabled: true, | 								enabled: true, | ||||||
| 							}, | 							}, | ||||||
|  | @ -334,7 +337,7 @@ export default defineComponent({ | ||||||
| 									max: 'original', | 									max: 'original', | ||||||
| 								}, | 								}, | ||||||
| 							} | 							} | ||||||
| 						}, | 						} : undefined, | ||||||
| 						gradient, | 						gradient, | ||||||
| 					}, | 					}, | ||||||
| 				}, | 				}, | ||||||
|  | @ -370,14 +373,14 @@ export default defineComponent({ | ||||||
| 			const raw = await os.api('charts/federation', { limit: props.limit, span: props.span }); | 			const raw = await os.api('charts/federation', { limit: props.limit, span: props.span }); | ||||||
| 			return { | 			return { | ||||||
| 				series: [{ | 				series: [{ | ||||||
| 					name: 'Total', | 					name: 'Sub', | ||||||
| 					type: 'area', | 					type: 'area', | ||||||
| 					data: format(raw.instance.total), | 					data: format(raw.sub), | ||||||
| 					color: '#888888', | 					color: colors.orange, | ||||||
| 				}, { | 				}, { | ||||||
| 					name: 'Inc/Dec', | 					name: 'Pub', | ||||||
| 					type: 'area', | 					type: 'area', | ||||||
| 					data: format(sum(raw.instance.inc, negate(raw.instance.dec))), | 					data: format(raw.pub), | ||||||
| 					color: colors.purple, | 					color: colors.purple, | ||||||
| 				}, { | 				}, { | ||||||
| 					name: 'Received', | 					name: 'Received', | ||||||
|  | @ -426,7 +429,6 @@ export default defineComponent({ | ||||||
| 				series: [{ | 				series: [{ | ||||||
| 					name: 'All', | 					name: 'All', | ||||||
| 					type: 'line', | 					type: 'line', | ||||||
| 					borderDash: [5, 5], |  | ||||||
| 					data: format(type == 'combined' | 					data: format(type == 'combined' | ||||||
| 						? sum(raw.local.inc, negate(raw.local.dec), raw.remote.inc, negate(raw.remote.dec)) | 						? sum(raw.local.inc, negate(raw.local.dec), raw.remote.inc, negate(raw.remote.dec)) | ||||||
| 						: sum(raw[type].inc, negate(raw[type].dec)) | 						: sum(raw[type].inc, negate(raw[type].dec)) | ||||||
|  | @ -750,20 +752,28 @@ export default defineComponent({ | ||||||
| 				series: [...(props.args.withoutAll ? [] : [{ | 				series: [...(props.args.withoutAll ? [] : [{ | ||||||
| 					name: 'All', | 					name: 'All', | ||||||
| 					type: 'line', | 					type: 'line', | ||||||
| 					borderDash: [5, 5], |  | ||||||
| 					data: format(sum(raw.inc, negate(raw.dec))), | 					data: format(sum(raw.inc, negate(raw.dec))), | ||||||
|  | 					color: '#888888', | ||||||
| 				}]), { | 				}]), { | ||||||
|  | 					name: 'With file', | ||||||
|  | 					type: 'area', | ||||||
|  | 					data: format(raw.diffs.withFile), | ||||||
|  | 					color: colors.purple, | ||||||
|  | 				}, { | ||||||
| 					name: 'Renotes', | 					name: 'Renotes', | ||||||
| 					type: 'area', | 					type: 'area', | ||||||
| 					data: format(raw.diffs.renote), | 					data: format(raw.diffs.renote), | ||||||
|  | 					color: colors.green, | ||||||
| 				}, { | 				}, { | ||||||
| 					name: 'Replies', | 					name: 'Replies', | ||||||
| 					type: 'area', | 					type: 'area', | ||||||
| 					data: format(raw.diffs.reply), | 					data: format(raw.diffs.reply), | ||||||
|  | 					color: colors.yellow, | ||||||
| 				}, { | 				}, { | ||||||
| 					name: 'Normal', | 					name: 'Normal', | ||||||
| 					type: 'area', | 					type: 'area', | ||||||
| 					data: format(raw.diffs.normal), | 					data: format(raw.diffs.normal), | ||||||
|  | 					color: colors.blue, | ||||||
| 				}], | 				}], | ||||||
| 			}; | 			}; | ||||||
| 		}; | 		}; | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| <template> | <template> | ||||||
| <div class="omfetrab" :class="['w' + width, 'h' + height, { big, asDrawer }]" :style="{ maxHeight: maxHeight ? maxHeight + 'px' : undefined }"> | <div class="omfetrab" :class="['s' + size, 'w' + width, 'h' + height, { asDrawer }]" :style="{ maxHeight: maxHeight ? maxHeight + 'px' : undefined }"> | ||||||
| 	<input ref="search" v-model.trim="q" class="search" data-prevent-emoji-insert :class="{ filled: q != null && q != '' }" :placeholder="i18n.ts.search" @paste.stop="paste" @keyup.enter="done()"> | 	<input ref="search" v-model.trim="q" class="search" data-prevent-emoji-insert :class="{ filled: q != null && q != '' }" :placeholder="i18n.ts.search" @paste.stop="paste" @keyup.enter="done()"> | ||||||
| 	<div ref="emojis" class="emojis"> | 	<div ref="emojis" class="emojis"> | ||||||
| 		<section class="result"> | 		<section class="result"> | ||||||
|  | @ -105,15 +105,16 @@ const emojis = ref<HTMLDivElement>(); | ||||||
| 
 | 
 | ||||||
| const { | const { | ||||||
| 	reactions: pinned, | 	reactions: pinned, | ||||||
|  | 	reactionPickerSize, | ||||||
| 	reactionPickerWidth, | 	reactionPickerWidth, | ||||||
| 	reactionPickerHeight, | 	reactionPickerHeight, | ||||||
| 	disableShowingAnimatedImages, | 	disableShowingAnimatedImages, | ||||||
| 	recentlyUsedEmojis, | 	recentlyUsedEmojis, | ||||||
| } = defaultStore.reactiveState; | } = defaultStore.reactiveState; | ||||||
| 
 | 
 | ||||||
|  | const size = computed(() => props.asReactionPicker ? reactionPickerSize.value : 1); | ||||||
| const width = computed(() => props.asReactionPicker ? reactionPickerWidth.value : 3); | const width = computed(() => props.asReactionPicker ? reactionPickerWidth.value : 3); | ||||||
| const height = computed(() => props.asReactionPicker ? reactionPickerHeight.value : 2); | const height = computed(() => props.asReactionPicker ? reactionPickerHeight.value : 2); | ||||||
| const big = props.asReactionPicker ? isTouchUsing : false; |  | ||||||
| const customEmojiCategories = emojiCategories; | const customEmojiCategories = emojiCategories; | ||||||
| const customEmojis = instance.emojis; | const customEmojis = instance.emojis; | ||||||
| const q = ref<string | null>(null); | const q = ref<string | null>(null); | ||||||
|  | @ -345,13 +346,20 @@ defineExpose({ | ||||||
| <style lang="scss" scoped> | <style lang="scss" scoped> | ||||||
| .omfetrab { | .omfetrab { | ||||||
| 	$pad: 8px; | 	$pad: 8px; | ||||||
| 	--eachSize: 40px; |  | ||||||
| 
 | 
 | ||||||
| 	display: flex; | 	display: flex; | ||||||
| 	flex-direction: column; | 	flex-direction: column; | ||||||
| 
 | 
 | ||||||
| 	&.big { | 	&.s1 { | ||||||
| 		--eachSize: 44px; | 		--eachSize: 40px; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	&.s2 { | ||||||
|  | 		--eachSize: 45px; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	&.s3 { | ||||||
|  | 		--eachSize: 50px; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	&.w1 { | 	&.w1 { | ||||||
|  | @ -369,6 +377,16 @@ defineExpose({ | ||||||
| 		--columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr; | 		--columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	&.w4 { | ||||||
|  | 		width: calc((var(--eachSize) * 8) + (#{$pad} * 2)); | ||||||
|  | 		--columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	&.w5 { | ||||||
|  | 		width: calc((var(--eachSize) * 9) + (#{$pad} * 2)); | ||||||
|  | 		--columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	&.h1 { | 	&.h1 { | ||||||
| 		height: calc((var(--eachSize) * 4) + (#{$pad} * 2)); | 		height: calc((var(--eachSize) * 4) + (#{$pad} * 2)); | ||||||
| 	} | 	} | ||||||
|  | @ -381,6 +399,10 @@ defineExpose({ | ||||||
| 		height: calc((var(--eachSize) * 8) + (#{$pad} * 2)); | 		height: calc((var(--eachSize) * 8) + (#{$pad} * 2)); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	&.h4 { | ||||||
|  | 		height: calc((var(--eachSize) * 10) + (#{$pad} * 2)); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	&.asDrawer { | 	&.asDrawer { | ||||||
| 		width: 100% !important; | 		width: 100% !important; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -59,7 +59,7 @@ export default defineComponent({ | ||||||
| 
 | 
 | ||||||
| 	setup() { | 	setup() { | ||||||
| 		const chartSpan = ref<'hour' | 'day'>('hour'); | 		const chartSpan = ref<'hour' | 'day'>('hour'); | ||||||
| 		const chartSrc = ref('notes'); | 		const chartSrc = ref('active-users'); | ||||||
| 
 | 
 | ||||||
| 		return { | 		return { | ||||||
| 			chartSrc, | 			chartSrc, | ||||||
|  |  | ||||||
|  | @ -17,17 +17,26 @@ | ||||||
| 		<template #caption>{{ $ts.reactionSettingDescription2 }} <button class="_textButton" @click="preview">{{ $ts.preview }}</button></template> | 		<template #caption>{{ $ts.reactionSettingDescription2 }} <button class="_textButton" @click="preview">{{ $ts.preview }}</button></template> | ||||||
| 	</FromSlot> | 	</FromSlot> | ||||||
| 
 | 
 | ||||||
| 	<FormRadios v-model="reactionPickerWidth" class="_formBlock"> | 	<FormRadios v-model="reactionPickerSize" class="_formBlock"> | ||||||
| 		<template #label>{{ $ts.width }}</template> | 		<template #label>{{ $ts.size }}</template> | ||||||
| 		<option :value="1">{{ $ts.small }}</option> | 		<option :value="1">{{ $ts.small }}</option> | ||||||
| 		<option :value="2">{{ $ts.medium }}</option> | 		<option :value="2">{{ $ts.medium }}</option> | ||||||
| 		<option :value="3">{{ $ts.large }}</option> | 		<option :value="3">{{ $ts.large }}</option> | ||||||
| 	</FormRadios> | 	</FormRadios> | ||||||
|  | 	<FormRadios v-model="reactionPickerWidth" class="_formBlock"> | ||||||
|  | 		<template #label>{{ $ts.numberOfColumn }}</template> | ||||||
|  | 		<option :value="1">5</option> | ||||||
|  | 		<option :value="2">6</option> | ||||||
|  | 		<option :value="3">7</option> | ||||||
|  | 		<option :value="4">8</option> | ||||||
|  | 		<option :value="5">9</option> | ||||||
|  | 	</FormRadios> | ||||||
| 	<FormRadios v-model="reactionPickerHeight" class="_formBlock"> | 	<FormRadios v-model="reactionPickerHeight" class="_formBlock"> | ||||||
| 		<template #label>{{ $ts.height }}</template> | 		<template #label>{{ $ts.height }}</template> | ||||||
| 		<option :value="1">{{ $ts.small }}</option> | 		<option :value="1">{{ $ts.small }}</option> | ||||||
| 		<option :value="2">{{ $ts.medium }}</option> | 		<option :value="2">{{ $ts.medium }}</option> | ||||||
| 		<option :value="3">{{ $ts.large }}</option> | 		<option :value="3">{{ $ts.large }}</option> | ||||||
|  | 		<option :value="4">{{ $ts.large }}+</option> | ||||||
| 	</FormRadios> | 	</FormRadios> | ||||||
| 
 | 
 | ||||||
| 	<FormSwitch v-model="reactionPickerUseDrawerForMobile" class="_formBlock"> | 	<FormSwitch v-model="reactionPickerUseDrawerForMobile" class="_formBlock"> | ||||||
|  | @ -60,6 +69,7 @@ import { i18n } from '@/i18n'; | ||||||
| 
 | 
 | ||||||
| let reactions = $ref(JSON.parse(JSON.stringify(defaultStore.state.reactions))); | let reactions = $ref(JSON.parse(JSON.stringify(defaultStore.state.reactions))); | ||||||
| 
 | 
 | ||||||
|  | const reactionPickerSize = $computed(defaultStore.makeGetterSetter('reactionPickerSize')); | ||||||
| const reactionPickerWidth = $computed(defaultStore.makeGetterSetter('reactionPickerWidth')); | const reactionPickerWidth = $computed(defaultStore.makeGetterSetter('reactionPickerWidth')); | ||||||
| const reactionPickerHeight = $computed(defaultStore.makeGetterSetter('reactionPickerHeight')); | const reactionPickerHeight = $computed(defaultStore.makeGetterSetter('reactionPickerHeight')); | ||||||
| const reactionPickerUseDrawerForMobile = $computed(defaultStore.makeGetterSetter('reactionPickerUseDrawerForMobile')); | const reactionPickerUseDrawerForMobile = $computed(defaultStore.makeGetterSetter('reactionPickerUseDrawerForMobile')); | ||||||
|  |  | ||||||
|  | @ -81,18 +81,65 @@ export default defineComponent({ | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
| 	async created() { | 	async created() { | ||||||
| 		this.softMutedWords = this.$store.state.mutedWords.map(x => x.join(' ')).join('\n'); | 		const render = (mutedWords) => mutedWords.map(x => { | ||||||
| 		this.hardMutedWords = this.$i.mutedWords.map(x => x.join(' ')).join('\n'); | 			if (Array.isArray(x)) { | ||||||
|  | 				return x.join(' '); | ||||||
|  | 			} else { | ||||||
|  | 				return x; | ||||||
|  | 			} | ||||||
|  | 		}).join('\n'); | ||||||
|  | 
 | ||||||
|  | 		this.softMutedWords = render(this.$store.state.mutedWords); | ||||||
|  | 		this.hardMutedWords = render(this.$i.mutedWords); | ||||||
| 
 | 
 | ||||||
| 		this.hardWordMutedNotesCount = (await os.api('i/get-word-muted-notes-count', {})).count; | 		this.hardWordMutedNotesCount = (await os.api('i/get-word-muted-notes-count', {})).count; | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
| 	methods: { | 	methods: { | ||||||
| 		async save() { | 		async save() { | ||||||
| 			this.$store.set('mutedWords', this.softMutedWords.trim().split('\n').map(x => x.trim().split(' '))); | 			const parseMutes = (mutes, tab) => { | ||||||
|  | 				// split into lines, remove empty lines and unnecessary whitespace | ||||||
|  | 				let lines = mutes.trim().split('\n').map(line => line.trim()).filter(line => line != ''); | ||||||
|  | 
 | ||||||
|  | 				// check each line if it is a RegExp or not | ||||||
|  | 				for(let i = 0; i < lines.length; i++) { | ||||||
|  | 					const line = lines[i] | ||||||
|  | 					const regexp = line.match(/^\/(.+)\/(.*)$/); | ||||||
|  | 					if (regexp) { | ||||||
|  | 						// check that the RegExp is valid | ||||||
|  | 						try { | ||||||
|  | 							new RegExp(regexp[1], regexp[2]); | ||||||
|  | 							// note that regex lines will not be split by spaces! | ||||||
|  | 						} catch (err) { | ||||||
|  | 							// invalid syntax: do not save, do not reset changed flag | ||||||
|  | 							os.alert({ | ||||||
|  | 								type: 'error', | ||||||
|  | 								title: this.$ts.regexpError, | ||||||
|  | 								text: this.$t('regexpErrorDescription', { tab, line: i + 1 }) + "\n" + err.toString() | ||||||
|  | 							}); | ||||||
|  | 							// re-throw error so these invalid settings are not saved | ||||||
|  | 							throw err; | ||||||
|  | 						} | ||||||
|  | 					} else { | ||||||
|  | 						lines[i] = line.split(' '); | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			}; | ||||||
|  | 
 | ||||||
|  | 			let softMutes, hardMutes; | ||||||
|  | 			try { | ||||||
|  | 				softMutes = parseMutes(this.softMutedWords, this.$ts._wordMute.soft); | ||||||
|  | 				hardMutes = parseMutes(this.hardMutedWords, this.$ts._wordMute.hard); | ||||||
|  | 			} catch (err) { | ||||||
|  | 				// already displayed error message in parseMutes | ||||||
|  | 				return; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			this.$store.set('mutedWords', softMutes); | ||||||
| 			await os.api('i/update', { | 			await os.api('i/update', { | ||||||
| 				mutedWords: this.hardMutedWords.trim().split('\n').map(x => x.trim().split(' ')), | 				mutedWords: hardMutes, | ||||||
| 			}); | 			}); | ||||||
|  | 
 | ||||||
| 			this.changed = false; | 			this.changed = false; | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -18,6 +18,6 @@ const props = withDefaults(defineProps<{ | ||||||
| 	user: misskey.entities.User; | 	user: misskey.entities.User; | ||||||
| 	limit?: number; | 	limit?: number; | ||||||
| }>(), { | }>(), { | ||||||
| 	limit: 40, | 	limit: 50, | ||||||
| }); | }); | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
|  | @ -1,23 +1,28 @@ | ||||||
| export function checkWordMute(note: Record<string, any>, me: Record<string, any> | null | undefined, mutedWords: string[][]): boolean { | export function checkWordMute(note: Record<string, any>, me: Record<string, any> | null | undefined, mutedWords: Array<string | string[]>): boolean { | ||||||
| 	// 自分自身
 | 	// 自分自身
 | ||||||
| 	if (me && (note.userId === me.id)) return false; | 	if (me && (note.userId === me.id)) return false; | ||||||
| 
 | 
 | ||||||
| 	const words = mutedWords | 	if (mutedWords.length > 0) { | ||||||
| 		// Clean up
 |  | ||||||
| 		.map(xs => xs.filter(x => x !== '')) |  | ||||||
| 		.filter(xs => xs.length > 0); |  | ||||||
| 
 |  | ||||||
| 	if (words.length > 0) { |  | ||||||
| 		if (note.text == null) return false; | 		if (note.text == null) return false; | ||||||
| 
 | 
 | ||||||
| 		const matched = words.some(and => | 		const matched = mutedWords.some(filter => { | ||||||
| 			and.every(keyword => { | 			if (Array.isArray(filter)) { | ||||||
| 				const regexp = keyword.match(/^\/(.+)\/(.*)$/); | 				return filter.every(keyword => note.text!.includes(keyword)); | ||||||
| 				if (regexp) { | 			} else { | ||||||
|  | 				// represents RegExp
 | ||||||
|  | 				const regexp = filter.match(/^\/(.+)\/(.*)$/); | ||||||
|  | 
 | ||||||
|  | 				// This should never happen due to input sanitisation.
 | ||||||
|  | 				if (!regexp) return false; | ||||||
|  | 
 | ||||||
|  | 				try { | ||||||
| 					return new RegExp(regexp[1], regexp[2]).test(note.text!); | 					return new RegExp(regexp[1], regexp[2]).test(note.text!); | ||||||
|  | 				} catch (err) { | ||||||
|  | 					// This should never happen due to input sanitisation.
 | ||||||
|  | 					return false; | ||||||
| 				} | 				} | ||||||
| 				return note.text!.includes(keyword); | 			} | ||||||
| 			})); | 		}); | ||||||
| 
 | 
 | ||||||
| 		if (matched) return true; | 		if (matched) return true; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -182,6 +182,10 @@ export const defaultStore = markRaw(new Storage('base', { | ||||||
| 		where: 'device', | 		where: 'device', | ||||||
| 		default: 'remote' as 'none' | 'remote' | 'always' | 		default: 'remote' as 'none' | 'remote' | 'always' | ||||||
| 	}, | 	}, | ||||||
|  | 	reactionPickerSize: { | ||||||
|  | 		where: 'device', | ||||||
|  | 		default: 1 | ||||||
|  | 	}, | ||||||
| 	reactionPickerWidth: { | 	reactionPickerWidth: { | ||||||
| 		where: 'device', | 		where: 'device', | ||||||
| 		default: 1 | 		default: 1 | ||||||
|  |  | ||||||
|  | @ -1322,11 +1322,11 @@ aws4@^1.8.0: | ||||||
|   integrity sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug== |   integrity sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug== | ||||||
| 
 | 
 | ||||||
| axios@^0.21.1: | axios@^0.21.1: | ||||||
|   version "0.21.1" |   version "0.21.4" | ||||||
|   resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8" |   resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575" | ||||||
|   integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA== |   integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== | ||||||
|   dependencies: |   dependencies: | ||||||
|     follow-redirects "^1.10.0" |     follow-redirects "^1.14.0" | ||||||
| 
 | 
 | ||||||
| babel-walk@3.0.0-canary-5: | babel-walk@3.0.0-canary-5: | ||||||
|   version "3.0.0-canary-5" |   version "3.0.0-canary-5" | ||||||
|  | @ -1426,38 +1426,16 @@ browser-stdout@1.3.1: | ||||||
|   resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" |   resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" | ||||||
|   integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== |   integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== | ||||||
| 
 | 
 | ||||||
| browserslist@^4.0.0, browserslist@^4.14.5: | browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.16.0, browserslist@^4.16.6: | ||||||
|   version "4.16.3" |   version "4.19.1" | ||||||
|   resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.3.tgz#340aa46940d7db878748567c5dea24a48ddf3717" |   resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.19.1.tgz#4ac0435b35ab655896c31d53018b6dd5e9e4c9a3" | ||||||
|   integrity sha512-vIyhWmIkULaq04Gt93txdh+j02yX/JzlyhLYbV3YQCn/zvES3JnY7TifHHvvr1w5hTDluNKMkV05cs4vy8Q7sw== |   integrity sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A== | ||||||
|   dependencies: |   dependencies: | ||||||
|     caniuse-lite "^1.0.30001181" |     caniuse-lite "^1.0.30001286" | ||||||
|     colorette "^1.2.1" |     electron-to-chromium "^1.4.17" | ||||||
|     electron-to-chromium "^1.3.649" |  | ||||||
|     escalade "^3.1.1" |     escalade "^3.1.1" | ||||||
|     node-releases "^1.1.70" |     node-releases "^2.0.1" | ||||||
| 
 |     picocolors "^1.0.0" | ||||||
| browserslist@^4.16.0: |  | ||||||
|   version "4.16.4" |  | ||||||
|   resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.4.tgz#7ebf913487f40caf4637b892b268069951c35d58" |  | ||||||
|   integrity sha512-d7rCxYV8I9kj41RH8UKYnvDYCRENUlHRgyXy/Rhr/1BaeLGfiCptEdFE8MIrvGfWbBFNjVYx76SQWvNX1j+/cQ== |  | ||||||
|   dependencies: |  | ||||||
|     caniuse-lite "^1.0.30001208" |  | ||||||
|     colorette "^1.2.2" |  | ||||||
|     electron-to-chromium "^1.3.712" |  | ||||||
|     escalade "^3.1.1" |  | ||||||
|     node-releases "^1.1.71" |  | ||||||
| 
 |  | ||||||
| browserslist@^4.16.6: |  | ||||||
|   version "4.16.6" |  | ||||||
|   resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.6.tgz#d7901277a5a88e554ed305b183ec9b0c08f66fa2" |  | ||||||
|   integrity sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ== |  | ||||||
|   dependencies: |  | ||||||
|     caniuse-lite "^1.0.30001219" |  | ||||||
|     colorette "^1.2.2" |  | ||||||
|     electron-to-chromium "^1.3.723" |  | ||||||
|     escalade "^3.1.1" |  | ||||||
|     node-releases "^1.1.71" |  | ||||||
| 
 | 
 | ||||||
| buffer-crc32@~0.2.3: | buffer-crc32@~0.2.3: | ||||||
|   version "0.2.13" |   version "0.2.13" | ||||||
|  | @ -1527,10 +1505,10 @@ caniuse-api@^3.0.0: | ||||||
|     lodash.memoize "^4.1.2" |     lodash.memoize "^4.1.2" | ||||||
|     lodash.uniq "^4.5.0" |     lodash.uniq "^4.5.0" | ||||||
| 
 | 
 | ||||||
| caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001181, caniuse-lite@^1.0.30001208, caniuse-lite@^1.0.30001219: | caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001286: | ||||||
|   version "1.0.30001279" |   version "1.0.30001311" | ||||||
|   resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001279.tgz" |   resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001311.tgz#682ef3f4e617f1a177ad943de59775ed3032e511" | ||||||
|   integrity sha512-VfEHpzHEXj6/CxggTwSFoZBBYGQfQv9Cf42KPlO79sWXCD1QNKWKsKzFeWL7QpZHJQYAvocqV6Rty1yJMkqWLQ== |   integrity sha512-mleTFtFKfykEeW34EyfhGIFjGCqzhh38Y0LhdQ9aWF+HorZTtdgKV/1hEE0NlFkG2ubvisPV6l400tlbPys98A== | ||||||
| 
 | 
 | ||||||
| caseless@~0.12.0: | caseless@~0.12.0: | ||||||
|   version "0.12.0" |   version "0.12.0" | ||||||
|  | @ -1722,7 +1700,7 @@ colord@^2.9.1: | ||||||
|   resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.1.tgz#c961ea0efeb57c9f0f4834458f26cb9cc4a3f90e" |   resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.1.tgz#c961ea0efeb57c9f0f4834458f26cb9cc4a3f90e" | ||||||
|   integrity sha512-4LBMSt09vR0uLnPVkOUBnmxgoaeN4ewRbx801wY/bXcltXfpR/G46OdWn96XpYmCWuYvO46aBZP4NgX8HpNAcw== |   integrity sha512-4LBMSt09vR0uLnPVkOUBnmxgoaeN4ewRbx801wY/bXcltXfpR/G46OdWn96XpYmCWuYvO46aBZP4NgX8HpNAcw== | ||||||
| 
 | 
 | ||||||
| colorette@^1.2.0, colorette@^1.2.1, colorette@^1.2.2: | colorette@^1.2.0, colorette@^1.2.2: | ||||||
|   version "1.2.2" |   version "1.2.2" | ||||||
|   resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" |   resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" | ||||||
|   integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== |   integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== | ||||||
|  | @ -2260,20 +2238,10 @@ ecdsa-sig-formatter@1.0.11: | ||||||
|   dependencies: |   dependencies: | ||||||
|     safe-buffer "^5.0.1" |     safe-buffer "^5.0.1" | ||||||
| 
 | 
 | ||||||
| electron-to-chromium@^1.3.649: | electron-to-chromium@^1.4.17: | ||||||
|   version "1.3.672" |   version "1.4.68" | ||||||
|   resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.672.tgz#3a6e335016dab4bc584d5292adc4f98f54541f6a" |   resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.68.tgz#d79447b6bd1bec9183f166bb33d4bef0d5e4e568" | ||||||
|   integrity sha512-gFQe7HBb0lbOMqK2GAS5/1F+B0IMdYiAgB9OT/w1F4M7lgJK2aNOMNOM622aEax+nS1cTMytkiT0uMOkbtFmHw== |   integrity sha512-cId+QwWrV8R1UawO6b9BR1hnkJ4EJPCPAr4h315vliHUtVUJDk39Sg1PMNnaWKfj5x+93ssjeJ9LKL6r8LaMiA== | ||||||
| 
 |  | ||||||
| electron-to-chromium@^1.3.712: |  | ||||||
|   version "1.3.717" |  | ||||||
|   resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.717.tgz#78d4c857070755fb58ab64bcc173db1d51cbc25f" |  | ||||||
|   integrity sha512-OfzVPIqD1MkJ7fX+yTl2nKyOE4FReeVfMCzzxQS+Kp43hZYwHwThlGP+EGIZRXJsxCM7dqo8Y65NOX/HP12iXQ== |  | ||||||
| 
 |  | ||||||
| electron-to-chromium@^1.3.723: |  | ||||||
|   version "1.3.742" |  | ||||||
|   resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.742.tgz#7223215acbbd3a5284962ebcb6df85d88b95f200" |  | ||||||
|   integrity sha512-ihL14knI9FikJmH2XUIDdZFWJxvr14rPSdOhJ7PpS27xbz8qmaRwCwyg/bmFwjWKmWK9QyamiCZVCvXm5CH//Q== |  | ||||||
| 
 | 
 | ||||||
| emoji-regex@^8.0.0: | emoji-regex@^8.0.0: | ||||||
|   version "8.0.0" |   version "8.0.0" | ||||||
|  | @ -2929,10 +2897,10 @@ flatted@^3.1.0: | ||||||
|   resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.1.0.tgz#a5d06b4a8b01e3a63771daa5cb7a1903e2e57067" |   resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.1.0.tgz#a5d06b4a8b01e3a63771daa5cb7a1903e2e57067" | ||||||
|   integrity sha512-tW+UkmtNg/jv9CSofAKvgVcO7c2URjhTdW1ZTkcAritblu8tajiYy7YisnIflEwtKssCtOxpnBRoCB7iap0/TA== |   integrity sha512-tW+UkmtNg/jv9CSofAKvgVcO7c2URjhTdW1ZTkcAritblu8tajiYy7YisnIflEwtKssCtOxpnBRoCB7iap0/TA== | ||||||
| 
 | 
 | ||||||
| follow-redirects@^1.10.0: | follow-redirects@^1.14.0: | ||||||
|   version "1.14.1" |   version "1.14.8" | ||||||
|   resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.1.tgz#d9114ded0a1cfdd334e164e6662ad02bfd91ff43" |   resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.8.tgz#016996fb9a11a100566398b1c6839337d7bfa8fc" | ||||||
|   integrity sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg== |   integrity sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA== | ||||||
| 
 | 
 | ||||||
| forever-agent@~0.6.1: | forever-agent@~0.6.1: | ||||||
|   version "0.6.1" |   version "0.6.1" | ||||||
|  | @ -3041,9 +3009,9 @@ getpass@^0.1.1: | ||||||
|     assert-plus "^1.0.0" |     assert-plus "^1.0.0" | ||||||
| 
 | 
 | ||||||
| glob-parent@^5.1.0, glob-parent@~5.1.0: | glob-parent@^5.1.0, glob-parent@~5.1.0: | ||||||
|   version "5.1.1" |   version "5.1.2" | ||||||
|   resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" |   resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" | ||||||
|   integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== |   integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== | ||||||
|   dependencies: |   dependencies: | ||||||
|     is-glob "^4.0.1" |     is-glob "^4.0.1" | ||||||
| 
 | 
 | ||||||
|  | @ -3490,14 +3458,7 @@ is-fullwidth-code-point@^3.0.0: | ||||||
|   resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" |   resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" | ||||||
|   integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== |   integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== | ||||||
| 
 | 
 | ||||||
| is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: | is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: | ||||||
|   version "4.0.1" |  | ||||||
|   resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" |  | ||||||
|   integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== |  | ||||||
|   dependencies: |  | ||||||
|     is-extglob "^2.1.1" |  | ||||||
| 
 |  | ||||||
| is-glob@^4.0.3: |  | ||||||
|   version "4.0.3" |   version "4.0.3" | ||||||
|   resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" |   resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" | ||||||
|   integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== |   integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== | ||||||
|  | @ -4245,19 +4206,21 @@ next-tick@~1.0.0: | ||||||
|   integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= |   integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= | ||||||
| 
 | 
 | ||||||
| node-fetch@^2.6.1: | node-fetch@^2.6.1: | ||||||
|   version "2.6.1" |   version "2.6.7" | ||||||
|   resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" |   resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" | ||||||
|   integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== |   integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== | ||||||
|  |   dependencies: | ||||||
|  |     whatwg-url "^5.0.0" | ||||||
| 
 | 
 | ||||||
| node-gyp-build@~3.7.0: | node-gyp-build@~3.7.0: | ||||||
|   version "3.7.0" |   version "3.7.0" | ||||||
|   resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-3.7.0.tgz#daa77a4f547b9aed3e2aac779eaf151afd60ec8d" |   resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-3.7.0.tgz#daa77a4f547b9aed3e2aac779eaf151afd60ec8d" | ||||||
|   integrity sha512-L/Eg02Epx6Si2NXmedx+Okg+4UHqmaf3TNcxd50SF9NQGcJaON3AtU++kax69XV7YWz4tUspqZSAsVofhFKG2w== |   integrity sha512-L/Eg02Epx6Si2NXmedx+Okg+4UHqmaf3TNcxd50SF9NQGcJaON3AtU++kax69XV7YWz4tUspqZSAsVofhFKG2w== | ||||||
| 
 | 
 | ||||||
| node-releases@^1.1.70, node-releases@^1.1.71: | node-releases@^2.0.1: | ||||||
|   version "1.1.71" |   version "2.0.2" | ||||||
|   resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.71.tgz#cb1334b179896b1c89ecfdd4b725fb7bbdfc7dbb" |   resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.2.tgz#7139fe71e2f4f11b47d4d2986aaf8c48699e0c01" | ||||||
|   integrity sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg== |   integrity sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg== | ||||||
| 
 | 
 | ||||||
| normalize-path@^3.0.0, normalize-path@~3.0.0: | normalize-path@^3.0.0, normalize-path@~3.0.0: | ||||||
|   version "3.0.0" |   version "3.0.0" | ||||||
|  | @ -4532,9 +4495,9 @@ path-key@^3.0.0, path-key@^3.1.0: | ||||||
|   integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== |   integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== | ||||||
| 
 | 
 | ||||||
| path-parse@^1.0.6: | path-parse@^1.0.6: | ||||||
|   version "1.0.6" |   version "1.0.7" | ||||||
|   resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" |   resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" | ||||||
|   integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== |   integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== | ||||||
| 
 | 
 | ||||||
| path-type@^4.0.0: | path-type@^4.0.0: | ||||||
|   version "4.0.0" |   version "4.0.0" | ||||||
|  | @ -5835,6 +5798,11 @@ tough-cookie@~2.5.0: | ||||||
|     psl "^1.1.28" |     psl "^1.1.28" | ||||||
|     punycode "^2.1.1" |     punycode "^2.1.1" | ||||||
| 
 | 
 | ||||||
|  | tr46@~0.0.3: | ||||||
|  |   version "0.0.3" | ||||||
|  |   resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" | ||||||
|  |   integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= | ||||||
|  | 
 | ||||||
| ts-loader@9.2.6: | ts-loader@9.2.6: | ||||||
|   version "9.2.6" |   version "9.2.6" | ||||||
|   resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.2.6.tgz#9937c4dd0a1e3dbbb5e433f8102a6601c6615d74" |   resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.2.6.tgz#9937c4dd0a1e3dbbb5e433f8102a6601c6615d74" | ||||||
|  | @ -6188,6 +6156,11 @@ web-push@3.4.5: | ||||||
|     minimist "^1.2.5" |     minimist "^1.2.5" | ||||||
|     urlsafe-base64 "^1.0.0" |     urlsafe-base64 "^1.0.0" | ||||||
| 
 | 
 | ||||||
|  | webidl-conversions@^3.0.0: | ||||||
|  |   version "3.0.1" | ||||||
|  |   resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" | ||||||
|  |   integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= | ||||||
|  | 
 | ||||||
| webpack-cli@4.9.1: | webpack-cli@4.9.1: | ||||||
|   version "4.9.1" |   version "4.9.1" | ||||||
|   resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.9.1.tgz#b64be825e2d1b130f285c314caa3b1ba9a4632b3" |   resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.9.1.tgz#b64be825e2d1b130f285c314caa3b1ba9a4632b3" | ||||||
|  | @ -6298,6 +6271,14 @@ websocket@1.0.34: | ||||||
|     utf-8-validate "^5.0.2" |     utf-8-validate "^5.0.2" | ||||||
|     yaeti "^0.0.6" |     yaeti "^0.0.6" | ||||||
| 
 | 
 | ||||||
|  | whatwg-url@^5.0.0: | ||||||
|  |   version "5.0.0" | ||||||
|  |   resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" | ||||||
|  |   integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= | ||||||
|  |   dependencies: | ||||||
|  |     tr46 "~0.0.3" | ||||||
|  |     webidl-conversions "^3.0.0" | ||||||
|  | 
 | ||||||
| which-boxed-primitive@^1.0.2: | which-boxed-primitive@^1.0.2: | ||||||
|   version "1.0.2" |   version "1.0.2" | ||||||
|   resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" |   resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue